import 'dart:io';
import 'dart:math' as math;
import 'package:flutter/material.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter/services.dart';

class ImageCanvas extends StatefulWidget {
  final String filePath;
  const ImageCanvas({super.key, required this.filePath});
  @override
  State<ImageCanvas> createState() => _ImageCanvasState();
}

class _ImageCanvasState extends State<ImageCanvas> {
  bool _isFilled = true;
  final GlobalKey _imageKey = GlobalKey();
  double _tx = 0, _ty = 0, _sc = 1.0;
  Size _vpSize = Size.zero;

  // ── Görüntü ayarları ──
  double _angle = 0;
  double _contrast = 1.0;
  double _saturation = 1.0;
  double _brightness = 0;

  // ── Gesture tracking ──
  String _interactionMode = 'none'; // 'contrast' | 'rotate' | 'saturation' | 'brightness'
  Offset _pointerDownPos = Offset.zero;
  double _startAngle = 0;
  double _startContrast = 1.0;
  double _startSaturation = 1.0;
  double _startBrightness = 0;
  DateTime? _lastRightTapTime;
  Offset _lastRightTapPos = Offset.zero;

  // ── Klavye modifier tracking ──
  bool _ctrlPressed = false;
  bool _shiftPressed = false;

  // ── Kare hızına eşitlenmiş render tick ──
  final ValueNotifier<int> _renderTick = ValueNotifier(0);
  bool _frameScheduled = false;

  // ── Shader warmup (ilk kullanım derlemesini açılışa çek) ──
  bool _warmupDone = false;
  int _warmupStep = 0; // 1=filtre, 2=filtre+rotate, 3=bitti

  void _markRenderDirty() {
    if (_frameScheduled) return;
    _frameScheduled = true;
    SchedulerBinding.instance.scheduleFrameCallback((_) {
      _frameScheduled = false;
      _renderTick.value++;
    });
  }

  void _clamp() {
    if (_vpSize.width <= 0 || _vpSize.height <= 0) return;
    if (_angle != 0) return; // rotate varsa serbest pan
    if (_isFilled) {
      final double w = _vpSize.width * _sc;
      final double h = _vpSize.height * _sc;
      if (w >= _vpSize.width) {
        if (_tx > 0) _tx = 0;
        else if (_tx + w < _vpSize.width) _tx = _vpSize.width - w;
      } else { if (_tx != 0) _tx = 0; }
      if (h >= _vpSize.height) {
        if (_ty > 0) _ty = 0;
        else if (_ty + h < _vpSize.height) _ty = _vpSize.height - h;
      } else { if (_ty != 0) _ty = 0; }
      return;
    }
    double m = 0, n = 0;
    double w = _vpSize.width * _sc;
    double h = _vpSize.height * _sc;
    final box = _imageKey.currentContext?.findRenderObject() as RenderBox?;
    if (box != null && box.hasSize && box.size.width > 0) {
      final Size img = box.size;
      m = (_vpSize.width - img.width) / 2;
      n = (_vpSize.height - img.height) / 2;
      w = img.width * _sc;
      h = img.height * _sc;
    }
    if (w >= _vpSize.width) {
      final double lo = -m * _sc;
      final double hi = _vpSize.width - m * _sc - w;
      if (lo <= hi) {
        if (_tx < lo) _tx = lo;
        if (_tx > hi) _tx = hi;
      } else {
        if (_tx < hi) _tx = hi;
        if (_tx > lo) _tx = lo;
      }
    } else {
      final double lo = -m * _sc;
      final double hi = _vpSize.width - m * _sc - w;
      if (_tx < lo) _tx = lo;
      if (_tx > hi) _tx = hi;
    }
    if (h >= _vpSize.height) {
      final double lo = -n * _sc;
      final double hi = _vpSize.height - n * _sc - h;
      if (lo <= hi) {
        if (_ty < lo) _ty = lo;
        if (_ty > hi) _ty = hi;
      } else {
        if (_ty < hi) _ty = hi;
        if (_ty > lo) _ty = lo;
      }
    } else {
      final double lo = -n * _sc;
      final double hi = _vpSize.height - n * _sc - h;
      if (_ty < lo) _ty = lo;
      if (_ty > hi) _ty = hi;
    }
  }

  @override
  void initState() {
    super.initState();
    HardwareKeyboard.instance.addHandler(_onHardwareKey);
    _startWarmup();
  }

  /// İlk kullanım shader derlemesini açılışa çek: gizli katmanda sırayla
  /// filtre ve rotate render edilir, sonra değerler sıfırlanır.
  void _startWarmup() {
    WidgetsBinding.instance.addPostFrameCallback((_) {
      if (!mounted) return;
      _warmupStep = 1;
      _markRenderDirty();
      WidgetsBinding.instance.addPostFrameCallback((_) => _warmupNext());
    });
  }

  void _warmupNext() {
    if (!mounted) return;
    if (_warmupStep >= 2) {
      _warmupDone = true;
      _markRenderDirty();
      return;
    }
    _warmupStep++;
    _markRenderDirty();
    WidgetsBinding.instance.addPostFrameCallback((_) => _warmupNext());
  }

  @override
  void dispose() {
    HardwareKeyboard.instance.removeHandler(_onHardwareKey);
    _renderTick.dispose();
    super.dispose();
  }

  bool _onHardwareKey(KeyEvent event) {
    final key = event.logicalKey;
    if (event is KeyDownEvent) {
      if (key == LogicalKeyboardKey.controlLeft ||
          key == LogicalKeyboardKey.controlRight) {
        _ctrlPressed = true;
      } else if (key == LogicalKeyboardKey.shiftLeft ||
                 key == LogicalKeyboardKey.shiftRight) {
        _shiftPressed = true;
      }
    } else if (event is KeyUpEvent) {
      if (key == LogicalKeyboardKey.controlLeft ||
          key == LogicalKeyboardKey.controlRight) {
        _ctrlPressed = false;
      } else if (key == LogicalKeyboardKey.shiftLeft ||
                 key == LogicalKeyboardKey.shiftRight) {
        _shiftPressed = false;
      }
    }
    return false;
  }

  @override
  void didUpdateWidget(ImageCanvas old) {
    super.didUpdateWidget(old);
    if (old.filePath != widget.filePath) _resetView();
  }

  void _resetView() {
    _tx = 0; _ty = 0; _sc = 1.0; _isFilled = true;
    _angle = 0; _contrast = 1.0; _saturation = 1.0; _brightness = 0;
    _markRenderDirty();
  }

  void _toggleFillFit() {
    _isFilled = !_isFilled; _tx = 0; _ty = 0; _sc = 1.0;
    _angle = 0; _contrast = 1.0; _saturation = 1.0; _brightness = 0;
    _markRenderDirty();
  }

  void _handleDoubleTap() => _toggleFillFit();

  void _handleScroll(PointerScrollEvent e) {
    if (_vpSize.width <= 0 || _vpSize.height <= 0) return;
    final double vx = _angle == 0 ? e.localPosition.dx : _vpSize.width / 2;
    final double vy = _angle == 0 ? e.localPosition.dy : _vpSize.height / 2;
    final double cx = (vx - _tx) / _sc;
    final double cy = (vy - _ty) / _sc;
    const double factor = 1.1;
    final double rawSc = _sc * (e.scrollDelta.dy < 0 ? factor : 1.0 / factor);
    final double newSc = rawSc.clamp(1.0, 10.0);
    if (_angle == 0) {
      _tx = _tx + cx * _sc - cx * newSc;
      _ty = _ty + cy * _sc - cy * newSc;
    } else {
      // Rotate'li zoom: viewport merkezini koru
      final double dw = _vpSize.width * (newSc - _sc);
      final double dh = _vpSize.height * (newSc - _sc);
      _tx -= dw / 2;
      _ty -= dh / 2;
    }
    _sc = newSc;
    _clamp();
    _markRenderDirty();
  }

  void _onPanUpdate(DragUpdateDetails d) {
    _tx += d.delta.dx;
    _ty += d.delta.dy;
    _clamp();
    _markRenderDirty();
  }

  void _onPointerDown(PointerDownEvent e) {
    if (e.buttons != kSecondaryMouseButton) return;

    _pointerDownPos = e.localPosition;

    final now = DateTime.now();
    if (_lastRightTapTime != null &&
        now.difference(_lastRightTapTime!) < const Duration(milliseconds: 400) &&
        (e.localPosition - _lastRightTapPos).distance < 20) {
      _angle = 0;
      _contrast = 1.0;
      _saturation = 1.0;
      _brightness = 0;
      _interactionMode = 'none';
      _markRenderDirty();
      _lastRightTapTime = null;
      return;
    }
    _lastRightTapTime = now;
    _lastRightTapPos = e.localPosition;

    if (_ctrlPressed && _shiftPressed) {
      _interactionMode = 'brightness';
      _startBrightness = _brightness;
    } else if (_ctrlPressed) {
      _interactionMode = 'saturation';
      _startSaturation = _saturation;
    } else if (_shiftPressed) {
      _interactionMode = 'rotate';
      _startAngle = _angle;
    } else {
      _interactionMode = 'contrast';
      _startContrast = _contrast;
    }
  }

  void _onPointerMove(PointerMoveEvent e) {
    if (e.buttons != kSecondaryMouseButton) return;

    final dy = e.localPosition.dy - _pointerDownPos.dy;

    switch (_interactionMode) {
      case 'contrast':
        _contrast = (_startContrast + dy * 0.005).clamp(0.0, 2.0);
        break;
      case 'rotate':
        _angle = _startAngle + dy * 0.9;
        break;
      case 'saturation':
        _saturation = (_startSaturation + dy * 0.005).clamp(0.0, 2.0);
        break;
      case 'brightness':
        _brightness = (_startBrightness + dy * 0.5).clamp(-100.0, 100.0);
        break;
    }
    _markRenderDirty();
  }

  void _onPointerUp(PointerUpEvent e) {
    _interactionMode = 'none';
  }

  // ── Combined color filter (brightness × saturation × contrast) ──

  static const double _rLum = 0.2126;
  static const double _gLum = 0.7152;
  static const double _bLum = 0.0722;

  ColorFilter? _cachedFilter;
  double _cachedSat = -1;
  double _cachedCon = -1;
  double _cachedBri = double.nan;

  List<double> _multiply4x5(List<double> a, List<double> b) {
    final result = List<double>.filled(20, 0.0);
    for (int row = 0; row < 4; row++) {
      for (int col = 0; col < 4; col++) {
        double sum = 0.0;
        for (int k = 0; k < 4; k++) {
          sum += a[row * 5 + k] * b[k * 5 + col];
        }
        result[row * 5 + col] = sum;
      }
      double offsetSum = a[row * 5 + 4];
      for (int k = 0; k < 4; k++) {
        offsetSum += a[row * 5 + k] * b[k * 5 + 4];
      }
      result[row * 5 + 4] = offsetSum;
    }
    return result;
  }

  ColorFilter _buildCombinedFilter() {
    if (_cachedFilter != null &&
        _cachedSat == _saturation &&
        _cachedCon == _contrast &&
        _cachedBri == _brightness) {
      return _cachedFilter!;
    }
    _cachedSat = _saturation;
    _cachedCon = _contrast;
    _cachedBri = _brightness;

    final s = _saturation;
    final c = _contrast;
    final b = _brightness;
    final invS = 1.0 - s;

    final sR_R = invS * _rLum + s, sR_G = invS * _gLum,     sR_B = invS * _bLum;
    final sG_R = invS * _rLum,     sG_G = invS * _gLum + s, sG_B = invS * _bLum;
    final sB_R = invS * _rLum,     sB_G = invS * _gLum,     sB_B = invS * _bLum + s;

    final saturationMatrix = <double>[
      sR_R, sR_G, sR_B, 0, 0,
      sG_R, sG_G, sG_B, 0, 0,
      sB_R, sB_G, sB_B, 0, 0,
      0,    0,    0,    1, 0,
    ];

    final brightnessMatrix = <double>[
      1, 0, 0, 0, b,
      0, 1, 0, 0, b,
      0, 0, 1, 0, b,
      0, 0, 0, 1, 0,
    ];

    final offset = 128.0 * (1.0 - c);
    final contrastMatrix = <double>[
      c, 0, 0, 0, offset,
      0, c, 0, 0, offset,
      0, 0, c, 0, offset,
      0, 0, 0, 1, 0,
    ];

    // M_final = M_contrast * M_brightness * M_saturation
    _cachedFilter = ColorFilter.matrix(
        _multiply4x5(_multiply4x5(contrastMatrix, brightnessMatrix), saturationMatrix));
    return _cachedFilter!;
  }

  FileImage? _cachedFileImage;
  ImageProvider<Object>? _cachedProvider;
  String? _cachedPath;
  int? _cachedCacheWidth;

  Widget _buildImage() {
    final longest = _vpSize.longestSide;
    final cacheWidth = longest > 0 ? (longest * 2).round() : null;
    if (_cachedPath != widget.filePath || _cachedCacheWidth != cacheWidth) {
      _cachedPath = widget.filePath;
      _cachedCacheWidth = cacheWidth;
      _cachedFileImage = FileImage(File(widget.filePath));
      _cachedProvider = cacheWidth != null
          ? ResizeImage.resizeIfNeeded(cacheWidth, null, _cachedFileImage!)
          : null;
    }
    final ImageProvider<Object> provider = _cachedProvider ?? _cachedFileImage!;
    return Image(
      key: _imageKey,
      image: provider,
      fit: _isFilled ? BoxFit.cover : BoxFit.contain,
      filterQuality: FilterQuality.high,
      errorBuilder: (_, _, _) => _errorWidget(),
    );
  }

  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(
      builder: (context, constraints) {
        _vpSize = constraints.biggest;
        _clamp();
        return RepaintBoundary(
          child: ValueListenableBuilder<int>(
            valueListenable: _renderTick,
            builder: (context, _, _) => _buildRenderLayer(),
          ),
        );
      },
    );
  }

  Widget _buildRenderLayer() {
    final vpCenterX = _vpSize.width / 2;
    final vpCenterY = _vpSize.height / 2;
    final angleRad = _angle * math.pi / 180;

    Widget imageContent = Builder(
      builder: (context) {
        final hasFilter = _saturation != 1.0 || _contrast != 1.0 || _brightness != 0.0;
        final image = _isFilled
            ? SizedBox.expand(child: _buildImage())
            : Center(child: _buildImage());
        return hasFilter
            ? ColorFiltered(colorFilter: _buildCombinedFilter(), child: image)
            : image;
      },
    );

    if (_angle != 0) {
      imageContent = Transform(
        alignment: Alignment.topLeft,
        transform: Matrix4.identity()
          ..translate(vpCenterX, vpCenterY)
          ..rotateZ(angleRad)
          ..translate(-vpCenterX, -vpCenterY),
        child: imageContent,
      );
    }

    return SizedBox.expand(
      child: Stack(
        children: [
          ClipRect(
            child: Listener(
              onPointerDown: _onPointerDown,
              onPointerMove: _onPointerMove,
              onPointerUp: _onPointerUp,
              onPointerSignal: (event) {
                if (event is PointerScrollEvent) _handleScroll(event);
              },
              child: GestureDetector(
                onDoubleTap: _handleDoubleTap,
                onPanUpdate: _onPanUpdate,
                child: Transform(
                  alignment: Alignment.topLeft,
                  transform: Matrix4.identity()
                    ..translate(_tx, _ty)
                    ..scale(_sc),
                  child: imageContent,
                ),
              ),
            ),
          ),
          _buildWarmupLayer(),
        ],
      ),
    );
  }

  /// Görünmez 1x1 katman — ColorFiltered ve Transform shader'larını açılışta
  /// derletir; kullanıcı ilk sağ tıkta derleme beklemesin.
  Widget _buildWarmupLayer() {
    if (_warmupDone) return const SizedBox.shrink();
    final filter = ColorFilter.matrix(const <double>[
      1.5, 0, 0, 0, 0,
      0, 1.5, 0, 0, 0,
      0, 0, 1.5, 0, 0,
      0, 0, 0, 1, 0,
    ]);
    Widget child = const SizedBox.expand();
    if (_warmupStep >= 2) {
      child = Transform(
        alignment: Alignment.center,
        transform: Matrix4.identity()..rotateZ(0.3),
        child: child,
      );
    }
    child = ColorFiltered(colorFilter: filter, child: child);
    return Opacity(
      opacity: 0,
      child: IgnorePointer(
        child: SizedBox(width: 1, height: 1, child: child),
      ),
    );
  }

  Widget _errorWidget() {
    return const Center(
      child: Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          Icon(Icons.broken_image, size: 48, color: Colors.white38),
          SizedBox(height: 8),
          Text('Resim yüklenemedi', style: TextStyle(color: Colors.white38)),
        ],
      ),
    );
  }
}
