import 'package:flutter/material.dart';

import '../../services/adb_service.dart';
import 'jenkins_web_login_page.dart';

class JenkinsWebLoginResult {
  const JenkinsWebLoginResult({
    required this.username,
    this.displayName,
    this.apiToken,
  });

  final String username;
  final String? displayName;
  // Non-null when an API token was issued via Web Login bridge.
  final String? apiToken;
}

typedef JenkinsWebLoginLauncher =
    Future<JenkinsWebLoginResult?> Function(String baseUrl);

// Called by SettingsPage when a token is issued via Web Login.
// Returns true if saving succeeded; false if it failed and was rolled back.
typedef JenkinsTokenSaver =
    Future<bool> Function(String baseUrl, String username, String apiToken);

typedef JenkinsSessionClearer = Future<void> Function();

class SettingsPage extends StatefulWidget {
  const SettingsPage({
    super.key,
    this.adbDiagnosticsLoader,
    this.webLoginLauncher,
    this.onWebLoginCompleted,
    this.onSessionSaved,
    this.tokenSaver,
    this.sessionClearer,
  });

  final AdbDiagnosticsLoader? adbDiagnosticsLoader;
  final JenkinsWebLoginLauncher? webLoginLauncher;
  final void Function(JenkinsWebLoginResult result)? onWebLoginCompleted;
  // Called after a token is successfully saved to Keychain so the app can
  // re-run session restore and activate the jobs loader.
  final VoidCallback? onSessionSaved;
  // Saves the issued token to Keychain. If null, token is not persisted.
  final JenkinsTokenSaver? tokenSaver;
  final JenkinsSessionClearer? sessionClearer;
  @override
  State<SettingsPage> createState() => _SettingsPageState();
}

class _SettingsPageState extends State<SettingsPage> {
  AdbRuntimeDiagnostics? _diagnostics;
  bool _loading = false;
  Object? _loadError;

  final _baseUrlController = TextEditingController();
  bool _webLoginLoading = false;
  Object? _webLoginError;
  JenkinsWebLoginResult? _lastLoginResult;

  // Token save state — set after Web Login dialog closes with a token.
  bool _tokenSaving = false;
  bool _tokenSaved = false;
  Object? _tokenSaveError;
  bool _sessionClearing = false;
  bool _sessionCleared = false;
  Object? _sessionClearError;

  @override
  void initState() {
    super.initState();
    _baseUrlController.addListener(_onBaseUrlChanged);
  }

  void _onBaseUrlChanged() {
    setState(() {
      _webLoginError = null;
      _lastLoginResult = null;
      _tokenSaved = false;
      _tokenSaveError = null;
      _sessionCleared = false;
      _sessionClearError = null;
    });
  }

  @override
  void dispose() {
    _baseUrlController.removeListener(_onBaseUrlChanged);
    _baseUrlController.dispose();
    super.dispose();
  }

  bool get _isValidJenkinsUrl {
    final text = _baseUrlController.text.trim();
    if (text.isEmpty) return false;
    try {
      final uri = Uri.parse(text);
      return (uri.scheme == 'http' || uri.scheme == 'https') &&
          uri.host.isNotEmpty;
    } catch (_) {
      return false;
    }
  }

  Future<void> _loadDiagnostics() async {
    final loader = widget.adbDiagnosticsLoader;
    if (loader == null) return;
    setState(() {
      _loading = true;
      _loadError = null;
    });
    try {
      final diag = await loader();
      if (!mounted) return;
      setState(() {
        _diagnostics = diag;
        _loading = false;
      });
    } catch (e) {
      if (!mounted) return;
      setState(() {
        _loadError = e;
        _loading = false;
      });
    }
  }

  Future<void> _handleWebLogin() async {
    if (!_isValidJenkinsUrl || _webLoginLoading) return;
    final baseUrl = _baseUrlController.text.trim();

    final launcher = widget.webLoginLauncher ?? _defaultLauncher;
    setState(() {
      _webLoginLoading = true;
      _webLoginError = null;
      _tokenSaved = false;
      _tokenSaveError = null;
      _sessionCleared = false;
      _sessionClearError = null;
    });
    try {
      final result = await launcher(baseUrl);
      if (!mounted) return;
      if (result != null) {
        if (result.apiToken == null) {
          setState(() {
            _webLoginError = 'API token 발급에 실패했습니다. Web Login을 다시 실행하세요.';
            _lastLoginResult = null;
            _webLoginLoading = false;
          });
          return;
        }

        widget.onWebLoginCompleted?.call(result);
        setState(() {
          _lastLoginResult = result;
          _webLoginLoading = false;
        });
        await _saveToken(baseUrl, result.username, result.apiToken!);
      } else {
        setState(() => _webLoginLoading = false);
      }
    } catch (e) {
      if (!mounted) return;
      setState(() {
        _webLoginError = e;
        _webLoginLoading = false;
      });
    }
  }

  Future<void> _saveToken(
    String baseUrl,
    String username,
    String apiToken,
  ) async {
    final saver = widget.tokenSaver;
    if (saver == null) return;
    setState(() {
      _tokenSaving = true;
      _tokenSaveError = null;
      _tokenSaved = false;
    });
    try {
      final ok = await saver(baseUrl, username, apiToken);
      if (!mounted) return;
      setState(() {
        _tokenSaving = false;
        _tokenSaved = ok;
        if (!ok) _tokenSaveError = 'token 저장에 실패했습니다';
      });
      if (ok) widget.onSessionSaved?.call();
    } catch (e) {
      if (!mounted) return;
      setState(() {
        _tokenSaving = false;
        _tokenSaveError = e;
      });
    }
  }

  Future<JenkinsWebLoginResult?> _defaultLauncher(String baseUrl) {
    return showDialog<JenkinsWebLoginResult>(
      context: context,
      barrierDismissible: false,
      builder: (_) =>
          Dialog.fullscreen(child: JenkinsWebLoginPage(baseUrl: baseUrl)),
    );
  }

  Future<void> _clearSession() async {
    final clearer = widget.sessionClearer;
    if (clearer == null || _sessionClearing) return;
    setState(() {
      _sessionClearing = true;
      _sessionCleared = false;
      _sessionClearError = null;
      _tokenSaved = false;
      _tokenSaveError = null;
      _lastLoginResult = null;
    });
    try {
      await clearer();
      if (!mounted) return;
      setState(() {
        _sessionClearing = false;
        _sessionCleared = true;
      });
    } catch (e) {
      if (!mounted) return;
      setState(() {
        _sessionClearing = false;
        _sessionClearError = e;
      });
    }
  }

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

    return SingleChildScrollView(
      padding: const EdgeInsets.fromLTRB(28, 0, 28, 28),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: [
          Material(
            color: Colors.white,
            shape: RoundedRectangleBorder(
              borderRadius: BorderRadius.circular(8),
              side: BorderSide(color: colorScheme.outlineVariant),
            ),
            child: Padding(
              padding: const EdgeInsets.all(18),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Text(
                    'Jenkins',
                    style: Theme.of(context).textTheme.titleMedium,
                  ),
                  const SizedBox(height: 14),
                  Row(
                    children: [
                      Expanded(
                        child: ListenableBuilder(
                          listenable: _baseUrlController,
                          builder: (context, _) => TextField(
                            controller: _baseUrlController,
                            decoration: InputDecoration(
                              labelText: 'Base URL',
                              prefixIcon: const Icon(Icons.dns_outlined),
                              errorText:
                                  _baseUrlController.text.isNotEmpty &&
                                      !_isValidJenkinsUrl
                                  ? '유효한 http/https URL을 입력하세요'
                                  : null,
                            ),
                          ),
                        ),
                      ),
                      const SizedBox(width: 12),
                      Expanded(
                        child: TextField(
                          decoration: const InputDecoration(
                            labelText: '기본 Job',
                            prefixIcon: Icon(Icons.account_tree_outlined),
                          ),
                        ),
                      ),
                    ],
                  ),
                  const SizedBox(height: 14),
                  SegmentedButton<String>(
                    segments: const [
                      ButtonSegment(
                        value: 'manual',
                        label: Text('Token'),
                        icon: Icon(Icons.key),
                      ),
                      ButtonSegment(
                        value: 'webview',
                        label: Text('Web Login'),
                        icon: Icon(Icons.web_asset),
                      ),
                    ],
                    selected: const {'webview'},
                    onSelectionChanged: (_) {},
                  ),
                  const SizedBox(height: 14),
                  _WebLoginSection(
                    isValidUrl: _isValidJenkinsUrl,
                    isLoading: _webLoginLoading,
                    error: _webLoginError,
                    lastResult: _lastLoginResult,
                    onLogin: _handleWebLogin,
                    tokenSaving: _tokenSaving,
                    tokenSaved: _tokenSaved,
                    tokenSaveError: _tokenSaveError,
                    canClearSession: widget.sessionClearer != null,
                    sessionClearing: _sessionClearing,
                    sessionCleared: _sessionCleared,
                    sessionClearError: _sessionClearError,
                    onClearSession: _clearSession,
                  ),
                ],
              ),
            ),
          ),
          const SizedBox(height: 14),
          Material(
            color: Colors.white,
            shape: RoundedRectangleBorder(
              borderRadius: BorderRadius.circular(8),
              side: BorderSide(color: colorScheme.outlineVariant),
            ),
            child: Padding(
              padding: const EdgeInsets.all(18),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Text('ADB', style: Theme.of(context).textTheme.titleMedium),
                  const SizedBox(height: 14),
                  Row(
                    children: [
                      Expanded(
                        child: TextField(
                          decoration: const InputDecoration(
                            labelText: 'adb path',
                            prefixIcon: Icon(Icons.terminal),
                          ),
                        ),
                      ),
                      const SizedBox(width: 12),
                      OutlinedButton.icon(
                        onPressed: () {},
                        icon: const Icon(Icons.search),
                        label: const Text('찾기'),
                      ),
                    ],
                  ),
                  const SizedBox(height: 8),
                  SwitchListTile(
                    contentPadding: EdgeInsets.zero,
                    value: true,
                    onChanged: (_) {},
                    title: const Text('설치 전 동일 패키지 업데이트'),
                    secondary: const Icon(Icons.update),
                  ),
                  if (widget.adbDiagnosticsLoader != null) ...[
                    const Divider(height: 24),
                    _AdbDiagnosticsSection(
                      diagnostics: _diagnostics,
                      loading: _loading,
                      loadError: _loadError,
                      onRefresh: _loadDiagnostics,
                    ),
                  ],
                ],
              ),
            ),
          ),
        ],
      ),
    );
  }
}

class _WebLoginSection extends StatelessWidget {
  const _WebLoginSection({
    required this.isValidUrl,
    required this.isLoading,
    required this.error,
    required this.lastResult,
    required this.onLogin,
    required this.tokenSaving,
    required this.tokenSaved,
    required this.tokenSaveError,
    required this.canClearSession,
    required this.sessionClearing,
    required this.sessionCleared,
    required this.sessionClearError,
    required this.onClearSession,
  });

  final bool isValidUrl;
  final bool isLoading;
  final Object? error;
  final JenkinsWebLoginResult? lastResult;
  final VoidCallback onLogin;
  final bool tokenSaving;
  final bool tokenSaved;
  final Object? tokenSaveError;
  final bool canClearSession;
  final bool sessionClearing;
  final bool sessionCleared;
  final Object? sessionClearError;
  final VoidCallback onClearSession;

  @override
  Widget build(BuildContext context) {
    final colorScheme = Theme.of(context).colorScheme;
    final textTheme = Theme.of(context).textTheme;
    final busy = isLoading || tokenSaving || sessionClearing;

    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Wrap(
          spacing: 8,
          runSpacing: 8,
          children: [
            ElevatedButton.icon(
              key: const ValueKey('jenkins-web-login-button'),
              onPressed: isValidUrl && !busy ? onLogin : null,
              icon: isLoading || tokenSaving
                  ? SizedBox(
                      width: 16,
                      height: 16,
                      child: CircularProgressIndicator(
                        strokeWidth: 2,
                        color: colorScheme.onPrimary,
                      ),
                    )
                  : const Icon(Icons.open_in_browser),
              label: const Text('Web Login'),
            ),
            OutlinedButton.icon(
              key: const ValueKey('jenkins-clear-session-button'),
              onPressed: canClearSession && !busy ? onClearSession : null,
              icon: sessionClearing
                  ? const SizedBox(
                      width: 16,
                      height: 16,
                      child: CircularProgressIndicator(strokeWidth: 2),
                    )
                  : const Icon(Icons.logout),
              label: const Text('Credential 삭제'),
            ),
          ],
        ),
        if (error != null) ...[
          const SizedBox(height: 8),
          Text(
            '로그인 오류: $error',
            style: textTheme.bodySmall?.copyWith(color: colorScheme.error),
          ),
        ],
        if (tokenSaveError != null) ...[
          const SizedBox(height: 8),
          Text(
            key: const ValueKey('token-save-error'),
            'token 저장 실패: $tokenSaveError',
            style: textTheme.bodySmall?.copyWith(color: colorScheme.error),
          ),
        ],
        if (sessionClearError != null) ...[
          const SizedBox(height: 8),
          Text(
            key: const ValueKey('session-clear-error'),
            'credential 삭제 실패: $sessionClearError',
            style: textTheme.bodySmall?.copyWith(color: colorScheme.error),
          ),
        ],
        if (sessionCleared) ...[
          const SizedBox(height: 8),
          Text(
            key: const ValueKey('session-cleared-label'),
            'credential 삭제 완료',
            style: textTheme.bodySmall?.copyWith(
              color: colorScheme.primary,
              fontWeight: FontWeight.w600,
            ),
          ),
        ],
        if (tokenSaved) ...[
          const SizedBox(height: 8),
          Text(
            key: const ValueKey('token-saved-label'),
            'token 저장 완료',
            style: textTheme.bodySmall?.copyWith(
              color: colorScheme.primary,
              fontWeight: FontWeight.w600,
            ),
          ),
        ],
        if (lastResult != null) ...[
          const SizedBox(height: 8),
          Text(
            key: const ValueKey('web-login-result'),
            lastResult!.displayName != null
                ? '로그인 완료: ${lastResult!.displayName}'
                : '로그인 완료: ${lastResult!.username}',
            style: textTheme.bodySmall?.copyWith(
              color: colorScheme.primary,
              fontWeight: FontWeight.w600,
            ),
          ),
        ],
      ],
    );
  }
}

class _AdbDiagnosticsSection extends StatelessWidget {
  const _AdbDiagnosticsSection({
    required this.diagnostics,
    required this.loading,
    required this.loadError,
    required this.onRefresh,
  });

  final AdbRuntimeDiagnostics? diagnostics;
  final bool loading;
  final Object? loadError;
  final VoidCallback onRefresh;

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

    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Row(
          children: [
            Text(
              'ADB 진단',
              style: textTheme.labelMedium?.copyWith(
                color: colorScheme.onSurfaceVariant,
              ),
            ),
            const SizedBox(width: 8),
            if (loading)
              Icon(
                key: const ValueKey('adb-diag-loading'),
                Icons.hourglass_empty,
                size: 14,
                color: colorScheme.onSurfaceVariant,
              )
            else
              InkWell(
                onTap: onRefresh,
                child: Icon(
                  Icons.refresh,
                  size: 16,
                  color: colorScheme.onSurfaceVariant,
                ),
              ),
          ],
        ),
        const SizedBox(height: 8),
        if (loadError != null)
          Text(
            '진단 로드 실패',
            style: textTheme.bodySmall?.copyWith(color: colorScheme.error),
          )
        else if (diagnostics == null && !loading)
          Text(
            '진단 정보 없음',
            style: textTheme.bodySmall?.copyWith(
              color: colorScheme.onSurfaceVariant,
            ),
          )
        else if (diagnostics != null)
          _DiagRows(diagnostics: diagnostics!),
      ],
    );
  }
}

class _DiagRows extends StatelessWidget {
  const _DiagRows({required this.diagnostics});

  final AdbRuntimeDiagnostics diagnostics;

  @override
  Widget build(BuildContext context) {
    final rt = diagnostics.runtime;
    final sha = diagnostics.sha256;
    final shortSha = sha != null && sha.length >= 12
        ? sha.substring(0, 12)
        : sha;

    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        _DiagRow(
          key: const ValueKey('diag-source'),
          label: '소스',
          value: _sourceLabel(rt.source, rt.fallbackUsed),
        ),
        _DiagRow(
          key: const ValueKey('diag-path'),
          label: '경로',
          value: rt.executablePath,
        ),
        _DiagRow(
          key: const ValueKey('diag-port'),
          label: '서버 포트',
          value: rt.serverPort.toString(),
        ),
        if (diagnostics.version != null)
          _DiagRow(
            key: const ValueKey('diag-version'),
            label: '버전',
            value: diagnostics.version!.split('\n').first,
          ),
        if (diagnostics.versionError != null)
          _DiagRow(
            key: const ValueKey('diag-version-error'),
            label: '버전 오류',
            value: diagnostics.versionError!,
            isError: true,
          ),
        if (shortSha != null)
          _DiagRow(
            key: const ValueKey('diag-hash'),
            label: 'SHA-256',
            value: shortSha,
          ),
      ],
    );
  }

  String _sourceLabel(AdbExecutableSource source, bool fallback) {
    return switch (source) {
      AdbExecutableSource.bundled => 'bundled',
      AdbExecutableSource.custom => 'custom',
      AdbExecutableSource.system => fallback ? 'system (fallback)' : 'system',
    };
  }
}

class _DiagRow extends StatelessWidget {
  const _DiagRow({
    super.key,
    required this.label,
    required this.value,
    this.isError = false,
  });

  final String label;
  final String value;
  final bool isError;

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

    return Padding(
      padding: const EdgeInsets.symmetric(vertical: 2),
      child: Row(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          SizedBox(
            width: 72,
            child: Text(
              label,
              style: textTheme.bodySmall?.copyWith(
                color: colorScheme.onSurfaceVariant,
              ),
            ),
          ),
          Expanded(
            child: Text(
              value,
              maxLines: 1,
              overflow: TextOverflow.ellipsis,
              style: textTheme.bodySmall?.copyWith(
                color: isError ? colorScheme.error : null,
                fontFamily: 'monospace',
              ),
            ),
          ),
        ],
      ),
    );
  }
}
