import 'dart:async';

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

import '../../models/adb_device.dart';
import '../../models/install_attempt_result.dart';
import '../../models/pending_install.dart';

typedef PendingInstallRunner =
    Future<void> Function(AdbDevice device, PendingInstall pending);
typedef AdbDeviceLoader = Future<List<AdbDevice>> Function();

class DevicesPage extends StatefulWidget {
  const DevicesPage({
    super.key,
    this.pendingInstall,
    this.onPendingInstallCancelled,
    this.installer,
    this.deviceLoader,
    this.isInstalling = false,
    this.lastInstallAttempt,
    this.onInstallAttemptCleared,
    this.onLogcatRequested,
  });

  final PendingInstall? pendingInstall;
  final VoidCallback? onPendingInstallCancelled;
  final PendingInstallRunner? installer;
  final AdbDeviceLoader? deviceLoader;
  final bool isInstalling;
  final InstallAttemptResult? lastInstallAttempt;
  final VoidCallback? onInstallAttemptCleared;
  final ValueChanged<AdbDevice>? onLogcatRequested;

  @override
  State<DevicesPage> createState() => _DevicesPageState();
}

class _DevicesPageState extends State<DevicesPage> {
  static const _filterAll = 'all';
  static const _filterReady = 'ready';
  static const _filterAttention = 'attention';

  Set<String> _selectedFilter = const {_filterAll};
  List<AdbDevice>? _loadedDevices;
  bool _isLoading = false;
  Object? _loadError;

  @override
  void initState() {
    super.initState();
    if (widget.deviceLoader != null) {
      unawaited(_refreshDevices());
    }
  }

  @override
  void didUpdateWidget(DevicesPage oldWidget) {
    super.didUpdateWidget(oldWidget);
    if (widget.deviceLoader != oldWidget.deviceLoader) {
      _loadedDevices = null;
      _loadError = null;
      if (widget.deviceLoader != null) {
        unawaited(_refreshDevices());
      }
    }
  }

  Future<void> _refreshDevices() async {
    final loader = widget.deviceLoader;
    if (loader == null) {
      setState(() {
        _loadedDevices = _sampleDevices;
        _loadError = null;
      });
      return;
    }

    setState(() {
      _isLoading = true;
      _loadError = null;
    });

    try {
      final devices = await loader();
      if (!mounted) return;
      setState(() {
        _loadedDevices = devices;
        _isLoading = false;
      });
    } catch (e) {
      if (!mounted) return;
      setState(() {
        _loadError = e;
        _isLoading = false;
      });
    }
  }

  List<AdbDevice> get _devices => widget.deviceLoader == null
      ? _sampleDevices
      : (_loadedDevices ?? const []);

  List<AdbDevice> get _filteredDevices {
    final selected = _selectedFilter.single;
    return switch (selected) {
      _filterReady => _devices.where((device) => device.isReady).toList(),
      _filterAttention => _devices.where((device) => !device.isReady).toList(),
      _ => _devices,
    };
  }

  @override
  Widget build(BuildContext context) {
    final devices = _filteredDevices;

    return Padding(
      padding: const EdgeInsets.fromLTRB(28, 0, 28, 28),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: [
          if (widget.pendingInstall != null) ...[
            _PendingInstallBanner(
              pendingInstall: widget.pendingInstall!,
              onCancel: widget.isInstalling
                  ? null
                  : widget.onPendingInstallCancelled,
            ),
            const SizedBox(height: 12),
          ],
          if (widget.lastInstallAttempt case final attempt?) ...[
            _InstallResultBanner(
              attempt: attempt,
              onClear: widget.onInstallAttemptCleared,
            ),
            const SizedBox(height: 12),
          ],
          Wrap(
            spacing: 12,
            runSpacing: 8,
            crossAxisAlignment: WrapCrossAlignment.center,
            children: [
              SegmentedButton<String>(
                segments: const [
                  ButtonSegment(
                    value: _filterAll,
                    label: Text('전체'),
                    icon: Icon(Icons.devices),
                  ),
                  ButtonSegment(
                    value: _filterReady,
                    label: Text('준비됨'),
                    icon: Icon(Icons.check_circle_outline),
                  ),
                  ButtonSegment(
                    value: _filterAttention,
                    label: Text('대기'),
                    icon: Icon(Icons.warning_amber_outlined),
                  ),
                ],
                selected: _selectedFilter,
                onSelectionChanged: (selection) {
                  setState(() => _selectedFilter = selection);
                },
              ),
              OutlinedButton.icon(
                key: const ValueKey('device-refresh-button'),
                onPressed: _isLoading ? null : _refreshDevices,
                icon: const Icon(Icons.refresh),
                label: const Text('새로고침'),
              ),
            ],
          ),
          if (_isLoading) ...[
            const SizedBox(height: 12),
            const LinearProgressIndicator(),
          ],
          if (_loadError != null) ...[
            const SizedBox(height: 12),
            _DeviceLoadError(onRetry: _refreshDevices),
          ],
          const SizedBox(height: 18),
          Expanded(
            child: devices.isEmpty && _isLoading
                ? const Center(child: CircularProgressIndicator())
                : devices.isEmpty
                ? const _EmptyDevicesView()
                : GridView.builder(
                    gridDelegate:
                        const SliverGridDelegateWithMaxCrossAxisExtent(
                          maxCrossAxisExtent: 360,
                          mainAxisExtent: 196,
                          mainAxisSpacing: 12,
                          crossAxisSpacing: 12,
                        ),
                    itemCount: devices.length,
                    itemBuilder: (context, index) {
                      return _DeviceTile(
                        device: devices[index],
                        pendingInstall: widget.pendingInstall,
                        isInstalling: widget.isInstalling,
                        installer: widget.installer,
                        onLogcatRequested: widget.onLogcatRequested,
                      );
                    },
                  ),
          ),
        ],
      ),
    );
  }
}

class _DeviceLoadError extends StatelessWidget {
  const _DeviceLoadError({required this.onRetry});

  final VoidCallback onRetry;

  @override
  Widget build(BuildContext context) {
    final colorScheme = Theme.of(context).colorScheme;

    return Material(
      key: const ValueKey('device-load-error'),
      color: colorScheme.errorContainer.withValues(alpha: 0.34),
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.circular(8),
        side: BorderSide(color: colorScheme.error.withValues(alpha: 0.32)),
      ),
      child: Padding(
        padding: const EdgeInsets.fromLTRB(14, 10, 10, 10),
        child: Row(
          children: [
            Icon(Icons.error_outline, color: colorScheme.error),
            const SizedBox(width: 10),
            Expanded(
              child: Text(
                'device 목록을 불러오지 못했습니다',
                maxLines: 1,
                overflow: TextOverflow.ellipsis,
                style: TextStyle(
                  color: colorScheme.error,
                  fontWeight: FontWeight.w700,
                ),
              ),
            ),
            IconButton(
              tooltip: '다시 시도',
              onPressed: onRetry,
              icon: const Icon(Icons.refresh),
            ),
          ],
        ),
      ),
    );
  }
}

class _EmptyDevicesView extends StatelessWidget {
  const _EmptyDevicesView();

  @override
  Widget build(BuildContext context) {
    final colorScheme = Theme.of(context).colorScheme;

    return Center(
      child: Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          Icon(Icons.usb_off_outlined, size: 40, color: colorScheme.secondary),
          const SizedBox(height: 8),
          Text(
            '연결된 device 없음',
            style: TextStyle(
              color: colorScheme.onSurfaceVariant,
              fontWeight: FontWeight.w700,
            ),
          ),
        ],
      ),
    );
  }
}

class _DeviceTile extends StatelessWidget {
  const _DeviceTile({
    required this.device,
    this.pendingInstall,
    required this.isInstalling,
    this.installer,
    this.onLogcatRequested,
  });

  final AdbDevice device;
  final PendingInstall? pendingInstall;
  final bool isInstalling;
  final PendingInstallRunner? installer;
  final ValueChanged<AdbDevice>? onLogcatRequested;

  @override
  Widget build(BuildContext context) {
    final colorScheme = Theme.of(context).colorScheme;
    final stateInfo = _DeviceStateInfo.from(device, colorScheme);
    final metadataLabel = _metadataLabel(device);

    final canInstall =
        pendingInstall != null &&
        installer != null &&
        device.isReady &&
        !isInstalling;

    return Material(
      key: ValueKey('device-tile-${device.serial}'),
      color: Colors.white,
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.circular(8),
        side: BorderSide(color: colorScheme.outlineVariant),
      ),
      child: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Row(
              children: [
                Icon(Icons.phone_android, color: stateInfo.color),
                const SizedBox(width: 8),
                Expanded(
                  child: Text(
                    device.displayName,
                    maxLines: 1,
                    overflow: TextOverflow.ellipsis,
                    style: Theme.of(context).textTheme.titleMedium,
                  ),
                ),
              ],
            ),
            const SizedBox(height: 12),
            Text(
              device.serial,
              maxLines: 1,
              overflow: TextOverflow.ellipsis,
              style: TextStyle(color: colorScheme.onSurfaceVariant),
            ),
            if (metadataLabel.isNotEmpty)
              Text(
                metadataLabel,
                maxLines: 1,
                overflow: TextOverflow.ellipsis,
                style: TextStyle(
                  color: colorScheme.onSurfaceVariant,
                  fontSize: 12,
                ),
              ),
            const Spacer(),
            Text(
              key: ValueKey('device-state-message-${device.serial}'),
              stateInfo.message,
              maxLines: 1,
              overflow: TextOverflow.ellipsis,
              style: TextStyle(
                color: colorScheme.onSurfaceVariant,
                fontSize: 12,
                fontWeight: FontWeight.w600,
              ),
            ),
            const SizedBox(height: 8),
            Row(
              children: [
                Flexible(child: _DeviceStateChip(info: stateInfo)),
                const SizedBox(width: 8),
                _CompactIconButton(
                  tooltip: 'logcat',
                  icon: Icons.terminal,
                  onPressed: device.isReady && onLogcatRequested != null
                      ? () => onLogcatRequested!(device)
                      : null,
                ),
                const SizedBox(width: 4),
                _CompactIconButton(
                  tooltip: '설치',
                  icon: isInstalling
                      ? Icons.hourglass_top
                      : Icons.install_mobile,
                  onPressed: canInstall
                      ? () => installer!(device, pendingInstall!)
                      : null,
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }

  String _metadataLabel(AdbDevice device) {
    final metadata = [
      if (device.product != null && device.product!.isNotEmpty)
        'product:${device.product}',
      if (device.device != null && device.device!.isNotEmpty)
        'device:${device.device}',
    ];
    return metadata.join(' · ');
  }
}

class _CompactIconButton extends StatelessWidget {
  const _CompactIconButton({
    required this.tooltip,
    required this.icon,
    this.onPressed,
  });

  final String tooltip;
  final IconData icon;
  final VoidCallback? onPressed;

  @override
  Widget build(BuildContext context) {
    return IconButton(
      tooltip: tooltip,
      constraints: const BoxConstraints.tightFor(width: 36, height: 36),
      padding: EdgeInsets.zero,
      onPressed: onPressed,
      icon: Icon(icon, size: 20),
    );
  }
}

class _DeviceStateChip extends StatelessWidget {
  const _DeviceStateChip({required this.info});

  final _DeviceStateInfo info;

  @override
  Widget build(BuildContext context) {
    return Container(
      height: 28,
      padding: const EdgeInsets.symmetric(horizontal: 10),
      alignment: Alignment.center,
      decoration: BoxDecoration(
        color: info.color.withValues(alpha: 0.12),
        borderRadius: BorderRadius.circular(8),
      ),
      child: Row(
        mainAxisSize: MainAxisSize.min,
        children: [
          Icon(info.icon, size: 14, color: info.color),
          const SizedBox(width: 5),
          Flexible(
            child: Text(
              key: info.key,
              info.label,
              maxLines: 1,
              overflow: TextOverflow.ellipsis,
              style: TextStyle(color: info.color, fontWeight: FontWeight.w800),
            ),
          ),
        ],
      ),
    );
  }
}

class _DeviceStateInfo {
  const _DeviceStateInfo({
    required this.label,
    required this.message,
    required this.icon,
    required this.color,
    required this.key,
  });

  final String label;
  final String message;
  final IconData icon;
  final Color color;
  final Key key;

  static _DeviceStateInfo from(AdbDevice device, ColorScheme colorScheme) {
    if (device.isReady) {
      return _DeviceStateInfo(
        label: 'ready',
        message: '설치 가능',
        icon: Icons.check_circle_outline,
        color: colorScheme.primary,
        key: ValueKey('device-state-${device.serial}'),
      );
    }

    if (device.needsAuthorization) {
      return _DeviceStateInfo(
        label: '승인 필요',
        message: '폰에서 USB debugging 승인 필요',
        icon: Icons.verified_user_outlined,
        color: colorScheme.error,
        key: ValueKey('device-state-${device.serial}'),
      );
    }

    if (device.isOffline) {
      return _DeviceStateInfo(
        label: 'offline',
        message: 'ADB 연결 대기',
        icon: Icons.hourglass_empty,
        color: colorScheme.secondary,
        key: ValueKey('device-state-${device.serial}'),
      );
    }

    return _DeviceStateInfo(
      label: device.state,
      message: '상태 확인 필요',
      icon: Icons.help_outline,
      color: colorScheme.onSurfaceVariant,
      key: ValueKey('device-state-${device.serial}'),
    );
  }
}

class _PendingInstallBanner extends StatelessWidget {
  const _PendingInstallBanner({required this.pendingInstall, this.onCancel});

  final PendingInstall pendingInstall;
  final VoidCallback? onCancel;

  @override
  Widget build(BuildContext context) {
    final colorScheme = Theme.of(context).colorScheme;
    final sizeLabel = pendingInstall.sizeBytes < 1024 * 1024
        ? '${(pendingInstall.sizeBytes / 1024).toStringAsFixed(1)} KB'
        : '${(pendingInstall.sizeBytes / (1024 * 1024)).toStringAsFixed(1)} MB';

    return Material(
      key: const ValueKey('pending-install-banner'),
      color: colorScheme.primaryContainer.withValues(alpha: 0.42),
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.circular(8),
        side: BorderSide(color: colorScheme.primary.withValues(alpha: 0.34)),
      ),
      child: Padding(
        padding: const EdgeInsets.fromLTRB(16, 12, 16, 12),
        child: Row(
          children: [
            Icon(Icons.install_mobile, color: colorScheme.primary),
            const SizedBox(width: 12),
            Expanded(
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Text(
                    key: const ValueKey('pending-install-filename'),
                    pendingInstall.fileName,
                    style: Theme.of(context).textTheme.bodyMedium?.copyWith(
                      fontWeight: FontWeight.w700,
                    ),
                    maxLines: 1,
                    overflow: TextOverflow.ellipsis,
                  ),
                  Text(
                    '#${pendingInstall.buildNumber} · ${pendingInstall.jobName} · $sizeLabel',
                    style: TextStyle(
                      color: colorScheme.onSurfaceVariant,
                      fontSize: 12,
                    ),
                    maxLines: 1,
                    overflow: TextOverflow.ellipsis,
                  ),
                  Text(
                    key: const ValueKey('pending-install-path'),
                    pendingInstall.apkPath,
                    style: TextStyle(
                      color: colorScheme.onSurfaceVariant,
                      fontSize: 11,
                    ),
                    maxLines: 1,
                    overflow: TextOverflow.ellipsis,
                  ),
                ],
              ),
            ),
            if (onCancel != null) ...[
              const SizedBox(width: 8),
              IconButton(
                key: const ValueKey('pending-install-cancel-button'),
                tooltip: '설치 대기 취소',
                onPressed: onCancel,
                icon: const Icon(Icons.close),
              ),
            ],
          ],
        ),
      ),
    );
  }
}

class _InstallResultBanner extends StatelessWidget {
  const _InstallResultBanner({required this.attempt, this.onClear});

  final InstallAttemptResult attempt;
  final VoidCallback? onClear;

  @override
  Widget build(BuildContext context) {
    final colorScheme = Theme.of(context).colorScheme;
    final isSuccess = attempt.succeeded;
    final color = isSuccess ? colorScheme.primary : colorScheme.error;
    final output = attempt.maskedOutputSummary;
    final metadataKey = ValueKey(
      'install-result-attempt:${attempt.pendingInstall.jobName}:'
      '${attempt.pendingInstall.buildNumber}:${attempt.pendingInstall.fileName}',
    );

    return Material(
      key: const ValueKey('install-result-banner'),
      color: color.withValues(alpha: 0.08),
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.circular(8),
        side: BorderSide(color: color.withValues(alpha: 0.34)),
      ),
      child: Padding(
        padding: const EdgeInsets.fromLTRB(16, 12, 16, 12),
        child: Row(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Icon(
              isSuccess ? Icons.check_circle_outline : Icons.error_outline,
              color: color,
            ),
            const SizedBox(width: 12),
            Expanded(
              child: Column(
                key: metadataKey,
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Text(
                    key: const ValueKey('current-install-result-title'),
                    '현재 설치 결과',
                    style: TextStyle(
                      color: colorScheme.onSurfaceVariant,
                      fontSize: 12,
                      fontWeight: FontWeight.w700,
                    ),
                    maxLines: 1,
                    overflow: TextOverflow.ellipsis,
                  ),
                  Text(
                    key: const ValueKey('install-result-label'),
                    isSuccess
                        ? '${attempt.device.displayName}에 설치 성공'
                        : '${attempt.device.displayName}에 설치 실패 (exit ${attempt.result.exitCode})',
                    style: Theme.of(context).textTheme.bodyMedium?.copyWith(
                      fontWeight: FontWeight.w700,
                      color: color,
                    ),
                    maxLines: 1,
                    overflow: TextOverflow.ellipsis,
                  ),
                  Text(
                    attempt.artifactLabel,
                    key: const ValueKey('install-result-metadata'),
                    style: TextStyle(
                      color: colorScheme.onSurfaceVariant,
                      fontSize: 12,
                      fontWeight: FontWeight.w600,
                    ),
                    maxLines: 1,
                    overflow: TextOverflow.ellipsis,
                  ),
                  if (output.isNotEmpty)
                    Text(
                      key: const ValueKey('install-result-output'),
                      output,
                      style: TextStyle(
                        color: colorScheme.onSurfaceVariant,
                        fontSize: 11,
                        fontFamily: 'monospace',
                      ),
                      maxLines: 4,
                      overflow: TextOverflow.ellipsis,
                    ),
                ],
              ),
            ),
            const SizedBox(width: 8),
            Column(
              mainAxisSize: MainAxisSize.min,
              children: [
                IconButton(
                  key: const ValueKey('install-report-copy-button'),
                  tooltip: '리포트 복사',
                  constraints: const BoxConstraints.tightFor(
                    width: 36,
                    height: 36,
                  ),
                  padding: EdgeInsets.zero,
                  onPressed: () => _copyReport(context),
                  icon: const Icon(Icons.content_copy, size: 20),
                ),
                IconButton(
                  key: const ValueKey('install-result-clear-button'),
                  tooltip: '현재 결과 초기화',
                  constraints: const BoxConstraints.tightFor(
                    width: 36,
                    height: 36,
                  ),
                  padding: EdgeInsets.zero,
                  onPressed: onClear,
                  icon: const Icon(Icons.close, size: 20),
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }

  Future<void> _copyReport(BuildContext context) async {
    await Clipboard.setData(ClipboardData(text: attempt.maskedReport));
    if (!context.mounted) return;
    ScaffoldMessenger.of(
      context,
    ).showSnackBar(const SnackBar(content: Text('리포트 복사됨')));
  }
}

const _sampleDevices = [
  AdbDevice(
    serial: 'R5CT90A1B2C',
    state: 'device',
    model: 'SM_S918N',
    product: 'dm3q',
  ),
  AdbDevice(
    serial: 'emulator-5554',
    state: 'device',
    model: 'sdk_gphone64_arm64',
    product: 'emu64a',
  ),
  AdbDevice(
    serial: 'ZX1G22K7Q9',
    state: 'unauthorized',
    model: 'Pixel_8',
    product: 'shiba',
  ),
];
