import 'dart:io';

import 'package:flutter/foundation.dart';
import 'package:image/image.dart' as img;

/// 撮影画像の保存・命名・削除を行うサービス．
class FileService {
  static const _basePath = '/storage/emulated/0/Pictures/MiniTIAS';

  /// 保存先ディレクトリのパスを返す．
  String get directoryPath => _basePath;

  /// Platform Channel から受け取った YUV データを PNG に変換して保存する．
  ///
  /// Camera2 API でフル解像度キャプチャした生データを直接変換するため，
  /// JPEG 圧縮を経由せず画質劣化がない．
  Future<String> saveImageFromYuvData(Map<String, dynamic> yuvData) async {
    final directory = Directory(_basePath);
    if (!await directory.exists()) {
      await directory.create(recursive: true);
    }

    final yRaw = yuvData['yPlane'];
    final uRaw = yuvData['uPlane'];
    final vRaw = yuvData['vPlane'];

    final Uint8List yPlane;
    final Uint8List uPlane;
    final Uint8List vPlane;

    if (yRaw is Uint8List) {
      yPlane = yRaw;
      uPlane = uRaw as Uint8List;
      vPlane = vRaw as Uint8List;
    } else {
      yPlane = Uint8List.fromList((yRaw as List).cast<int>());
      uPlane = Uint8List.fromList((uRaw as List).cast<int>());
      vPlane = Uint8List.fromList((vRaw as List).cast<int>());
    }

    debugPrint(
      'YUV data: ${yuvData['width']}x${yuvData['height']}, '
      'yPlane: ${yPlane.length} bytes, '
      'uPlane: ${uPlane.length} bytes, '
      'vPlane: ${vPlane.length} bytes',
    );

    if (yPlane.isEmpty) {
      throw Exception('YUV データが空です');
    }

    final params = {
      'width': yuvData['width'] as int,
      'height': yuvData['height'] as int,
      'yPlane': yPlane,
      'uPlane': uPlane,
      'vPlane': vPlane,
      'yRowStride': yuvData['yRowStride'] as int,
      'uvRowStride': yuvData['uvRowStride'] as int,
      'uvPixelStride': yuvData['uvPixelStride'] as int,
    };

    debugPrint('PNG 変換開始...');
    final stopwatch = Stopwatch()..start();
    final pngBytes = await compute(_convertYuvToPng, params);
    stopwatch.stop();
    debugPrint(
      'PNG 変換完了: ${pngBytes.length} bytes, '
      '${stopwatch.elapsedMilliseconds}ms',
    );

    final fileName = await generateFileName();
    final filePath = '$_basePath/$fileName';
    final file = File(filePath);
    await file.writeAsBytes(pngBytes);
    debugPrint('ファイル保存完了: $filePath');

    return filePath;
  }

  /// ファイル名を生成する．
  ///
  /// 形式: MiniTIAS_YYYYMMDD_HHmmss.png
  /// 同秒の重複がある場合は _1, _2, ... と連番を付与する．
  Future<String> generateFileName() async {
    final now = DateTime.now();
    final timestamp =
        '${now.year}'
        '${now.month.toString().padLeft(2, '0')}'
        '${now.day.toString().padLeft(2, '0')}'
        '_'
        '${now.hour.toString().padLeft(2, '0')}'
        '${now.minute.toString().padLeft(2, '0')}'
        '${now.second.toString().padLeft(2, '0')}';

    final baseName = 'MiniTIAS_$timestamp';
    final candidate = '$baseName.png';

    if (!await File('$_basePath/$candidate').exists()) {
      return candidate;
    }

    var suffix = 1;
    while (await File('$_basePath/${baseName}_$suffix.png').exists()) {
      suffix++;
    }
    return '${baseName}_$suffix.png';
  }

  /// テスト用: タイムスタンプとファイル名一覧からファイル名を生成する．
  static String generateFileNameSync(
    String timestamp,
    List<String> existingFiles,
  ) {
    final baseName = 'MiniTIAS_$timestamp';
    final candidate = '$baseName.png';

    if (!existingFiles.contains(candidate)) {
      return candidate;
    }

    var suffix = 1;
    while (existingFiles.contains('${baseName}_$suffix.png')) {
      suffix++;
    }
    return '${baseName}_$suffix.png';
  }

  /// YUV420 を RGB に変換し PNG エンコードする（isolate 内で実行）．
  static Uint8List _convertYuvToPng(Map<String, dynamic> params) {
    final width = params['width'] as int;
    final height = params['height'] as int;
    final yPlane = params['yPlane'] as Uint8List;
    final uPlane = params['uPlane'] as Uint8List;
    final vPlane = params['vPlane'] as Uint8List;
    final yRowStride = params['yRowStride'] as int;
    final uvRowStride = params['uvRowStride'] as int;
    final uvPixelStride = params['uvPixelStride'] as int;

    final image = img.Image(width: width, height: height);

    for (var y = 0; y < height; y++) {
      for (var x = 0; x < width; x++) {
        final yIndex = y * yRowStride + x;
        final uvIndex = (y ~/ 2) * uvRowStride + (x ~/ 2) * uvPixelStride;

        final yValue = yPlane[yIndex];
        final uValue = uPlane[uvIndex];
        final vValue = vPlane[uvIndex];

        final r = (yValue + 1.402 * (vValue - 128)).round().clamp(0, 255);
        final g = (yValue - 0.344 * (uValue - 128) - 0.714 * (vValue - 128))
            .round()
            .clamp(0, 255);
        final b = (yValue + 1.772 * (uValue - 128)).round().clamp(0, 255);

        image.setPixelRgb(x, y, r, g, b);
      }
    }

    return Uint8List.fromList(img.encodePng(image));
  }
}
