import 'dart:async';
import 'dart:io';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
import 'package:path_provider/path_provider.dart';
import 'package:share_plus/share_plus.dart';

import '../../core/api/pocketbase_client.dart';
import '../../core/theme/app_theme.dart';
import '../../models/job.dart';
import '../../models/job_file.dart';
import 'job_files_repository.dart';

const _maxFileSizeBytes = 50 * 1024 * 1024; // 50 MB per file
const _maxFilesPerJob = 10;

class JobFilesPanel extends StatefulWidget {
  const JobFilesPanel({
    super.key,
    required this.job,
    required this.filesFuture,
    required this.onRefresh,
  });

  final Job job;
  final Future<List<JobFile>> filesFuture;
  final VoidCallback onRefresh;

  @override
  State<JobFilesPanel> createState() => _JobFilesPanelState();
}

class _JobFilesPanelState extends State<JobFilesPanel> {
  _UploadState? _upload;
  List<JobFile>? _files;
  bool _loadingFiles = false;
  String? _filesError;

  @override
  void initState() {
    super.initState();
    _subscribeToFuture(widget.filesFuture);
  }

  @override
  void didUpdateWidget(JobFilesPanel oldWidget) {
    super.didUpdateWidget(oldWidget);
    if (oldWidget.filesFuture != widget.filesFuture) {
      _subscribeToFuture(widget.filesFuture);
    }
  }

  void _subscribeToFuture(Future<List<JobFile>> future) {
    setState(() { _loadingFiles = true; _filesError = null; });
    future.then((files) {
      if (mounted) setState(() { _files = files; _loadingFiles = false; });
    }).catchError((e) {
      if (mounted) setState(() { _filesError = _friendlyError(e); _loadingFiles = false; });
    });
  }

  static String _friendlyError(Object e) {
    final s = e.toString();
    // Strip full ClientException URL dumps — show only the message part
    final msgMatch = RegExp(r'message: ([^,}]+)').firstMatch(s);
    if (msgMatch != null) return msgMatch.group(1)!.trim();
    if (s.length > 100) return 'Sunucu hatası';
    return s;
  }

  Future<void> _pickAndUpload() async {
    final result = await FilePicker.platform.pickFiles(
      allowMultiple: true,
      withData: true,
      type: FileType.custom,
      allowedExtensions: [
        'pdf', 'jpg', 'jpeg', 'png', 'webp',
        'stl', 'obj', 'ply', 'zip', 'opus', 'mp3', 'mp4'
      ],
    );
    if (result == null || result.files.isEmpty || !mounted) return;

    // Client-side size validation
    for (final pf in result.files) {
      if (pf.size > _maxFileSizeBytes) {
        ScaffoldMessenger.of(context).showSnackBar(SnackBar(
          content: Text('${pf.name} 50 MB sınırını aşıyor (${_formatSize(pf.size)}).'),
          backgroundColor: AppColors.cancelled,
        ));
        return;
      }
    }

    final existingCount = _files?.length ?? 0;
    final remaining = _maxFilesPerJob - existingCount;
    if (result.files.length > remaining) {
      if (!mounted) return;
      ScaffoldMessenger.of(context).showSnackBar(SnackBar(
        content: Text('Bu iş en fazla $_maxFilesPerJob dosya alabilir. Şu an $existingCount dosya var; en fazla $remaining dosya daha ekleyebilirsiniz.'),
        backgroundColor: AppColors.cancelled,
      ));
      return;
    }

    int uploadedCount = 0;
    for (var i = 0; i < result.files.length; i++) {
      final pf = result.files[i];
      if (pf.bytes == null) continue;

      setState(() {
        _upload = _UploadState(
          fileName: pf.name,
          fileIndex: i + 1,
          totalFiles: result.files.length,
          progress: 0,
          speedBytesPerSec: 0,
        );
      });

      try {
        await _uploadWithProgress(
          pf: pf,
          onProgress: (progress, speed) {
            if (mounted) {
              setState(() {
                _upload = _upload?.copyWith(progress: progress, speedBytesPerSec: speed);
              });
            }
          },
        );
        uploadedCount++;
      } catch (e) {
        if (mounted) {
          ScaffoldMessenger.of(context).showSnackBar(
            SnackBar(content: Text('${pf.name} yüklenemedi: ${_friendlyError(e)}'), backgroundColor: AppColors.cancelled),
          );
        }
        setState(() => _upload = null);
        return;
      }
    }

    setState(() => _upload = null);
    widget.onRefresh();
    if (mounted && uploadedCount > 0) {
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('$uploadedCount dosya yüklendi.')),
      );
    }
  }

  Future<void> _uploadWithProgress({
    required PlatformFile pf,
    required void Function(double progress, double speedBytesPerSec) onProgress,
  }) async {
    final bytes = pf.bytes!;
    final pb = PocketBaseClient.instance.pb;
    final baseUrl = 'https://pocket.kovaksoft.com';
    final uri = Uri.parse('$baseUrl/api/collections/job_files/records');

    final ext = (pf.extension ?? '').toLowerCase();
    final kind = (ext == 'stl' || ext == 'obj' || ext == 'ply')
        ? JobFileKind.scan
        : (ext == 'pdf') ? JobFileKind.document : JobFileKind.image;
    final mimeType = _mimeFromExt(ext);
    final currentUserId = (pb.authStore.record?.id) ?? '';
    final token = pb.authStore.token;

    final startTime = DateTime.now().millisecondsSinceEpoch;
    int sentBytes = 0;

    Stream<List<int>> progressStream(List<int> src) async* {
      const chunkSize = 65536;
      var offset = 0;
      while (offset < src.length) {
        final end = (offset + chunkSize).clamp(0, src.length);
        final chunk = src.sublist(offset, end);
        yield chunk;
        offset = end;
        sentBytes = offset;
        final elapsedMs = DateTime.now().millisecondsSinceEpoch - startTime;
        final speed = elapsedMs > 0 ? sentBytes / elapsedMs * 1000 : 0.0;
        onProgress(sentBytes / src.length, speed);
        // yield control so Flutter can rebuild
        await Future<void>.delayed(Duration.zero);
      }
    }

    final request = http.MultipartRequest('POST', uri)
      ..headers['Authorization'] = 'Bearer $token'
      ..fields['job_id'] = widget.job.id
      ..fields['clinic_tenant_id'] = widget.job.clinicTenantId
      ..fields['lab_tenant_id'] = widget.job.labTenantId
      ..fields['uploaded_by'] = currentUserId
      ..fields['kind'] = kind.value
      ..fields['name'] = pf.name
      ..fields['size'] = bytes.length.toString()
      ..fields['mime_type'] = mimeType
      ..files.add(http.MultipartFile(
        'file',
        http.ByteStream(progressStream(bytes)),
        bytes.length,
        filename: pf.name,
      ));

    final streamed = await request.send();
    final body = await streamed.stream.bytesToString();

    if (streamed.statusCode < 200 || streamed.statusCode >= 300) {
      String msg = 'HTTP ${streamed.statusCode}';
      try {
        final j = jsonDecode(body) as Map<String, dynamic>;
        msg = j['message'] as String? ?? msg;
      } catch (_) {}
      throw Exception(msg);
    }
  }

  Future<void> _bulkDownload(List<JobFile> files) async {
    if (files.isEmpty) return;
    final messenger = ScaffoldMessenger.of(context);
    try {
      final pb = PocketBaseClient.instance.pb;
      final fileToken = await pb.files.getToken();
      final dir = await getTemporaryDirectory();
      await dir.create(recursive: true);

      // Download all files in parallel
      final results = await Future.wait(
        files.where((f) => f.downloadUrl.isNotEmpty).map((file) async {
          final uri = Uri.parse('${file.downloadUrl}?token=$fileToken');
          final response = await http.get(uri);
          if (response.statusCode != 200) return null;
          final path = '${dir.path}/${file.name}';
          await File(path).writeAsBytes(response.bodyBytes);
          return XFile(path, mimeType: file.mimeType ?? 'application/octet-stream');
        }),
      );

      final xFiles = results.whereType<XFile>().toList();
      if (xFiles.isEmpty) return;
      await Share.shareXFiles(
        xFiles,
        subject: '${widget.job.patientCode} dosyaları',
        sharePositionOrigin: const Rect.fromLTWH(100, 100, 200, 1),
      );
    } catch (e) {
      if (mounted) {
        messenger.showSnackBar(
          SnackBar(content: Text('İndirilemedi: $e'), backgroundColor: AppColors.cancelled),
        );
      }
    }
  }

  Future<void> _deleteFile(JobFile file) async {
    final confirmed = await showDialog<bool>(
      context: context,
      builder: (ctx) => AlertDialog(
        title: const Text('Dosyayı Sil'),
        content: Text(file.name),
        actions: [
          TextButton(onPressed: () => Navigator.pop(ctx, false), child: const Text('İptal')),
          FilledButton(
            style: FilledButton.styleFrom(backgroundColor: AppColors.cancelled),
            onPressed: () => Navigator.pop(ctx, true),
            child: const Text('Sil'),
          ),
        ],
      ),
    );
    if (confirmed != true || !mounted) return;

    // Optimistic: remove immediately from local list
    setState(() => _files = _files?.where((f) => f.id != file.id).toList());

    try {
      await JobFilesRepository.instance.deleteFile(file.id);
    } catch (e) {
      final is404 = e.toString().contains('404') || e.toString().contains('wasn\'t found');
      if (!is404) {
        // Revert only on transient errors (network, 500) — not when already deleted
        setState(() => _files = [...?_files, file]);
      }
      if (mounted) {
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(
            content: Text(is404 ? 'Dosya zaten silinmiş.' : 'Silinemedi: ${_friendlyError(e)}'),
            backgroundColor: AppColors.cancelled,
          ),
        );
      }
    }
  }

  Future<void> _downloadFile(JobFile file, Rect shareOrigin) async {
    if (file.downloadUrl.isEmpty) return;
    final messenger = ScaffoldMessenger.of(context);
    try {
      final pb = PocketBaseClient.instance.pb;
      final fileToken = await pb.files.getToken();
      final uri = Uri.parse('${file.downloadUrl}?token=$fileToken');
      final response = await http.get(uri);
      if (response.statusCode != 200) {
        throw Exception('HTTP ${response.statusCode}');
      }
      final dir = await getTemporaryDirectory();
      await dir.create(recursive: true);
      final path = '${dir.path}/${file.name}';
      await File(path).writeAsBytes(response.bodyBytes);
      await Share.shareXFiles(
        [XFile(path, mimeType: file.mimeType ?? 'application/octet-stream')],
        subject: file.name,
        sharePositionOrigin: shareOrigin,
      );
    } catch (e) {
      if (mounted) {
        messenger.showSnackBar(
          SnackBar(
            content: Text('İndirilemedi: $e'),
            backgroundColor: AppColors.cancelled,
          ),
        );
      }
    }
  }

  @override
  Widget build(BuildContext context) {
    final uploading = _upload != null;
    return Container(
      padding: const EdgeInsets.all(16),
      decoration: BoxDecoration(
        color: AppColors.surface,
        borderRadius: BorderRadius.circular(16),
        border: Border.all(color: AppColors.border),
      ),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Row(
            children: [
              const Icon(Icons.attach_file_rounded, size: 18, color: AppColors.accent),
              const SizedBox(width: 6),
              const Expanded(
                child: Text('Dosyalar', style: TextStyle(fontWeight: FontWeight.w600, fontSize: 14)),
              ),
              if (!uploading) ...[
                if ((_files?.length ?? 0) >= 2)
                  TextButton.icon(
                    onPressed: () => _bulkDownload(_files!),
                    icon: const Icon(Icons.download_for_offline_outlined, size: 16),
                    label: const Text('Hepsini İndir'),
                    style: TextButton.styleFrom(
                      foregroundColor: AppColors.accent,
                      padding: const EdgeInsets.symmetric(horizontal: 8),
                    ),
                  ),
                TextButton.icon(
                  onPressed: _pickAndUpload,
                  icon: const Icon(Icons.upload_rounded, size: 16),
                  label: const Text('Yükle'),
                  style: TextButton.styleFrom(
                    foregroundColor: AppColors.accent,
                    padding: const EdgeInsets.symmetric(horizontal: 8),
                  ),
                ),
              ],
            ],
          ),

          if (_upload != null) ...[
            const SizedBox(height: 10),
            _UploadProgressBar(state: _upload!),
          ],

          const SizedBox(height: 8),
          Builder(builder: (ctx) {
            if (_loadingFiles && _files == null) {
              return const Padding(
                padding: EdgeInsets.symmetric(vertical: 12),
                child: Center(child: CircularProgressIndicator(strokeWidth: 2)),
              );
            }
            if (_filesError != null && _files == null) {
              return Padding(
                padding: const EdgeInsets.symmetric(vertical: 8),
                child: Text(
                  'Dosyalar yüklenemedi: $_filesError',
                  style: const TextStyle(color: AppColors.cancelled, fontSize: 12),
                ),
              );
            }
            final files = _files ?? [];
            if (files.isEmpty && !uploading) {
              return const Padding(
                padding: EdgeInsets.symmetric(vertical: 8),
                child: Text(
                  'Henüz dosya eklenmemiş.',
                  style: TextStyle(color: AppColors.textMuted, fontSize: 13),
                ),
              );
            }
            return Column(
              children: files
                  .map((f) => _FileRow(
                        file: f,
                        onDelete: () => _deleteFile(f),
                        onDownload: (origin) => _downloadFile(f, origin),
                      ))
                  .toList(),
            );
          },
          ),
        ],
      ),
    );
  }

  static String _formatSize(int bytes) {
    if (bytes < 1024) return '$bytes B';
    if (bytes < 1024 * 1024) return '${(bytes / 1024).toStringAsFixed(1)} KB';
    return '${(bytes / (1024 * 1024)).toStringAsFixed(1)} MB';
  }

  static String _mimeFromExt(String ext) => switch (ext) {
        'jpg' || 'jpeg' => 'image/jpeg',
        'png' => 'image/png',
        'webp' => 'image/webp',
        'pdf' => 'application/pdf',
        'stl' => 'model/stl',
        'zip' => 'application/zip',
        'mp3' => 'audio/mpeg',
        'mp4' => 'video/mp4',
        'opus' => 'audio/opus',
        _ => 'application/octet-stream',
      };
}

// ── Upload Progress Bar ───────────────────────────────────────────────────────

class _UploadProgressBar extends StatelessWidget {
  const _UploadProgressBar({required this.state});
  final _UploadState state;

  @override
  Widget build(BuildContext context) {
    final pct = (state.progress * 100).toStringAsFixed(0);
    final speed = state.speedBytesPerSec;
    final speedLabel = speed >= 1024 * 1024
        ? '${(speed / 1024 / 1024).toStringAsFixed(1)} MB/s'
        : '${(speed / 1024).toStringAsFixed(0)} KB/s';

    final fileLabel = state.totalFiles > 1
        ? '${state.fileIndex}/${state.totalFiles} — ${state.fileName}'
        : state.fileName;

    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Row(
          children: [
            Expanded(
              child: Text(
                fileLabel,
                style: const TextStyle(fontSize: 12, color: AppColors.textSecondary),
                maxLines: 1,
                overflow: TextOverflow.ellipsis,
              ),
            ),
            const SizedBox(width: 8),
            Text(
              '$pct% · $speedLabel',
              style: const TextStyle(
                fontSize: 12,
                fontWeight: FontWeight.w600,
                color: AppColors.accent,
                fontFeatures: [FontFeature.tabularFigures()],
              ),
            ),
          ],
        ),
        const SizedBox(height: 6),
        ClipRRect(
          borderRadius: BorderRadius.circular(4),
          child: LinearProgressIndicator(
            value: state.progress,
            minHeight: 6,
            backgroundColor: AppColors.background,
            valueColor: const AlwaysStoppedAnimation<Color>(AppColors.accent),
          ),
        ),
      ],
    );
  }
}

// ── File Row ──────────────────────────────────────────────────────────────────

class _FileRow extends StatefulWidget {
  const _FileRow({
    required this.file,
    required this.onDelete,
    required this.onDownload,
  });
  final JobFile file;
  final VoidCallback onDelete;
  final Future<void> Function(Rect) onDownload;

  @override
  State<_FileRow> createState() => _FileRowState();
}

class _FileRowState extends State<_FileRow> {
  bool _downloading = false;

  IconData get _icon => switch (widget.file.kind) {
        JobFileKind.scan => Icons.view_in_ar_rounded,
        JobFileKind.image => Icons.image_outlined,
        JobFileKind.document => Icons.description_outlined,
      };

  final _downloadKey = GlobalKey();

  Future<void> _handleDownload() async {
    setState(() => _downloading = true);
    final box = _downloadKey.currentContext?.findRenderObject() as RenderBox?;
    final origin = box != null
        ? box.localToGlobal(Offset.zero) & box.size
        : const Rect.fromLTWH(100, 100, 200, 1);
    try {
      await widget.onDownload(origin);
    } finally {
      if (mounted) setState(() => _downloading = false);
    }
  }

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.symmetric(vertical: 5),
      child: Row(
        children: [
          Icon(_icon, size: 18, color: AppColors.textMuted),
          const SizedBox(width: 8),
          Expanded(
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text(
                  widget.file.name,
                  style: const TextStyle(fontSize: 13, fontWeight: FontWeight.w500),
                  maxLines: 1,
                  overflow: TextOverflow.ellipsis,
                ),
                Text(
                  '${widget.file.kind.label} · ${widget.file.sizeLabel}',
                  style: const TextStyle(fontSize: 11, color: AppColors.textMuted),
                ),
              ],
            ),
          ),
          if (_downloading)
            const SizedBox(
              width: 18,
              height: 18,
              child: CircularProgressIndicator(strokeWidth: 2, color: AppColors.accent),
            )
          else
            IconButton(
              key: _downloadKey,
              onPressed: _handleDownload,
              icon: const Icon(Icons.download_outlined, size: 18, color: AppColors.accent),
              padding: EdgeInsets.zero,
              constraints: const BoxConstraints(),
              tooltip: 'İndir',
            ),
          const SizedBox(width: 4),
          IconButton(
            onPressed: widget.onDelete,
            icon: const Icon(Icons.delete_outline_rounded, size: 18, color: AppColors.cancelled),
            padding: EdgeInsets.zero,
            constraints: const BoxConstraints(),
            tooltip: 'Sil',
          ),
        ],
      ),
    );
  }
}

// ── Upload State ──────────────────────────────────────────────────────────────

class _UploadState {
  const _UploadState({
    required this.fileName,
    required this.fileIndex,
    required this.totalFiles,
    required this.progress,
    required this.speedBytesPerSec,
  });

  final String fileName;
  final int fileIndex;
  final int totalFiles;
  final double progress;
  final double speedBytesPerSec;

  _UploadState copyWith({double? progress, double? speedBytesPerSec}) =>
      _UploadState(
        fileName: fileName,
        fileIndex: fileIndex,
        totalFiles: totalFiles,
        progress: progress ?? this.progress,
        speedBytesPerSec: speedBytesPerSec ?? this.speedBytesPerSec,
      );
}
