// Version: 1.0.0 | Created: 2026-04-11
// Security setup dialog: drives the Matrix SDK Bootstrap to set up
// SSSS, cross-signing, and online key backup in one flow.
// For new users: creates everything, shows recovery key.
// For existing users: unlocks with recovery key, restores message keys.

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:matrix/encryption/ssss.dart';
import 'package:matrix/encryption/utils/bootstrap.dart';
import 'package:matrix/matrix.dart';

/// Shows the security setup dialog. Returns true if setup completed.
Future<bool> showSecuritySetupDialog(
    BuildContext context, Client client) async {
  final result = await showDialog<bool>(
    context: context,
    barrierDismissible: false,
    builder: (_) => _SecuritySetupDialog(client: client),
  );
  return result ?? false;
}

class _SecuritySetupDialog extends StatefulWidget {
  const _SecuritySetupDialog({required this.client});
  final Client client;

  @override
  State<_SecuritySetupDialog> createState() => _SecuritySetupDialogState();
}

enum _Phase {
  checking,
  needsRecoveryKey,
  working,
  showRecoveryKey,
  restoring,
  done,
  error,
}

class _SecuritySetupDialogState extends State<_SecuritySetupDialog> {
  final _keyController = TextEditingController();
  _Phase _phase = _Phase.checking;
  String? _error;
  String? _recoveryKey;
  String _statusMessage = 'Checking security setup...';
  late Bootstrap _bootstrap;

  @override
  void initState() {
    super.initState();
    _start();
  }

  @override
  void dispose() {
    _keyController.dispose();
    super.dispose();
  }

  Future<void> _start() async {
    final enc = widget.client.encryption;
    if (enc == null) {
      _setError('Encryption is not available in this browser.');
      return;
    }

    _bootstrap = enc.bootstrap(onUpdate: (_) {});

    try {
      await _driveBootstrap();
    } catch (e) {
      _setError(e.toString().split('\n').first);
    }
  }

  Future<void> _driveBootstrap() async {
    while (_bootstrap.state != BootstrapState.done &&
        _bootstrap.state != BootstrapState.error) {
      debugPrint('[Security] Bootstrap state: ${_bootstrap.state}');

      switch (_bootstrap.state) {
        case BootstrapState.loading:
          setState(() {
            _phase = _Phase.working;
            _statusMessage = 'Setting up...';
          });
          // Wait for the bootstrap to advance via onUpdate
          await Future.delayed(const Duration(milliseconds: 200));

        case BootstrapState.askWipeSsss:
          // Existing SSSS found — don't wipe, use it
          setState(() {
            _phase = _Phase.working;
            _statusMessage = 'Found existing security setup...';
          });
          _bootstrap.wipeSsss(false);

        case BootstrapState.askUseExistingSsss:
          // Use the existing SSSS
          _bootstrap.useExistingSsss(true);

        case BootstrapState.openExistingSsss:
          // Need the recovery key from the user
          if (!_bootstrap.newSsssKey!.isUnlocked) {
            setState(() => _phase = _Phase.needsRecoveryKey);
            return; // Wait for user input
          }
          // Already unlocked, continue
          setState(() {
            _phase = _Phase.working;
            _statusMessage = 'Verifying security keys...';
          });
          await _bootstrap.openExistingSsss();

        case BootstrapState.askBadSsss:
          // Ignore bad secrets and continue
          _bootstrap.ignoreBadSecrets(true);

        case BootstrapState.askUnlockSsss:
          // Old keys need unlocking — this is a migration scenario
          if (_bootstrap.oldSsssKeys != null) {
            for (final key in _bootstrap.oldSsssKeys!.values) {
              if (!key.isUnlocked) {
                setState(() => _phase = _Phase.needsRecoveryKey);
                return; // Wait for user input
              }
            }
          }
          _bootstrap.unlockedSsss();

        case BootstrapState.askNewSsss:
          // Create new SSSS (no passphrase — recovery key only)
          setState(() {
            _phase = _Phase.working;
            _statusMessage = 'Creating security keys...';
          });
          await _bootstrap.newSsss();

        case BootstrapState.askSetupCrossSigning:
          setState(() => _statusMessage = 'Setting up cross-signing...');
          await _bootstrap.askSetupCrossSigning(
            setupMasterKey: true,
            setupSelfSigningKey: true,
            setupUserSigningKey: true,
          );

        case BootstrapState.askWipeCrossSigning:
          // Don't wipe existing cross-signing
          await _bootstrap.wipeCrossSigning(false);

        case BootstrapState.askSetupOnlineKeyBackup:
          setState(() => _statusMessage = 'Creating key backup...');
          await _bootstrap.askSetupOnlineKeyBackup(true);

        case BootstrapState.askWipeOnlineKeyBackup:
          // Don't wipe existing backup
          _bootstrap.wipeOnlineKeyBackup(false);

        case BootstrapState.done:
        case BootstrapState.error:
          break; // Exit loop
      }
    }

    if (_bootstrap.state == BootstrapState.error) {
      _setError('Setup failed. Please try again.');
      return;
    }

    // Success — show recovery key if we created new SSSS
    _recoveryKey = _bootstrap.newSsssKey?.recoveryKey;

    if (_recoveryKey != null && _phase != _Phase.needsRecoveryKey) {
      // New setup — show the recovery key
      setState(() => _phase = _Phase.showRecoveryKey);
    } else {
      // Existing setup — restore keys from backup
      await _restoreKeys();
    }
  }

  /// Called when the user enters their recovery key for existing SSSS.
  Future<void> _unlockWithRecoveryKey() async {
    final key = _keyController.text.trim();
    if (key.isEmpty) return;

    setState(() {
      _phase = _Phase.working;
      _statusMessage = 'Verifying recovery key...';
    });

    try {
      // Unlock whichever SSSS key is waiting
      if (_bootstrap.newSsssKey != null && !_bootstrap.newSsssKey!.isUnlocked) {
        await _bootstrap.newSsssKey!.unlock(keyOrPassphrase: key);
      }
      if (_bootstrap.oldSsssKeys != null) {
        for (final ssssKey in _bootstrap.oldSsssKeys!.values) {
          if (!ssssKey.isUnlocked) {
            await ssssKey.unlock(keyOrPassphrase: key);
          }
        }
      }
    } on InvalidPassphraseException {
      _setError('Invalid recovery key. Please check and try again.');
      return;
    } on Exception catch (e) {
      _setError('Could not unlock: ${e.toString().split('\n').first}');
      return;
    }

    // Continue the bootstrap
    try {
      await _driveBootstrap();
    } catch (e) {
      _setError(e.toString().split('\n').first);
    }
  }

  /// Download all keys from the online key backup after SSSS is unlocked.
  Future<void> _restoreKeys() async {
    setState(() {
      _phase = _Phase.restoring;
      _statusMessage = 'Downloading message keys from backup...';
    });

    try {
      await widget.client.encryption?.keyManager.loadAllKeys();
      debugPrint('[Security] All keys loaded from backup');
    } on MatrixException catch (e) {
      if (e.errcode != 'M_NOT_FOUND') {
        debugPrint('[Security] Key restore error: ${e.errorMessage}');
      }
      // M_NOT_FOUND just means no backup exists yet — not an error
    } on Exception catch (e) {
      debugPrint('[Security] Key restore error: $e');
    }

    setState(() => _phase = _Phase.done);
  }

  void _setError(String message) {
    setState(() {
      _phase = _Phase.error;
      _error = message;
    });
  }

  @override
  Widget build(BuildContext context) {
    return AlertDialog(
      title: Text(switch (_phase) {
        _Phase.checking => 'Security Setup',
        _Phase.needsRecoveryKey => 'Enter Recovery Key',
        _Phase.working || _Phase.restoring => 'Setting Up...',
        _Phase.showRecoveryKey => 'Save Your Recovery Key',
        _Phase.done => 'Security Setup Complete',
        _Phase.error => 'Setup Error',
      }),
      content: SizedBox(
        width: 400,
        child: switch (_phase) {
          _Phase.checking || _Phase.working || _Phase.restoring => Column(
              mainAxisSize: MainAxisSize.min,
              children: [
                const CircularProgressIndicator(),
                const SizedBox(height: 16),
                Text(_statusMessage, textAlign: TextAlign.center),
              ],
            ),
          _Phase.needsRecoveryKey => Column(
              mainAxisSize: MainAxisSize.min,
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                const Text(
                  'Enter your recovery key to unlock your encrypted '
                  'messages. This is the key shown when you first set '
                  'up encryption in Element.',
                ),
                const SizedBox(height: 16),
                TextField(
                  controller: _keyController,
                  decoration: const InputDecoration(
                    hintText: 'EsTc oRgW rqHN...',
                    labelText: 'Recovery key or passphrase',
                    border: OutlineInputBorder(),
                  ),
                  maxLines: 3,
                  onSubmitted: (_) => _unlockWithRecoveryKey(),
                ),
              ],
            ),
          _Phase.showRecoveryKey => Column(
              mainAxisSize: MainAxisSize.min,
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                const Text(
                  'Your recovery key has been created. Save it somewhere '
                  'safe — you will need it to restore your messages on '
                  'new devices.',
                ),
                const SizedBox(height: 16),
                Container(
                  width: double.infinity,
                  padding: const EdgeInsets.all(16),
                  decoration: BoxDecoration(
                    color: Theme.of(context)
                        .colorScheme
                        .surfaceContainerHighest,
                    borderRadius: BorderRadius.circular(8),
                    border: Border.all(
                      color: Theme.of(context).colorScheme.outline,
                    ),
                  ),
                  child: SelectableText(
                    _recoveryKey ?? '',
                    style: const TextStyle(
                      fontFamily: 'monospace',
                      fontSize: 14,
                      letterSpacing: 1.2,
                    ),
                  ),
                ),
                const SizedBox(height: 12),
                Row(
                  children: [
                    const Icon(Icons.warning_amber, size: 16),
                    const SizedBox(width: 8),
                    Expanded(
                      child: Text(
                        'If you lose this key, you will not be able to '
                        'read your encrypted messages on new devices.',
                        style: Theme.of(context).textTheme.bodySmall,
                      ),
                    ),
                  ],
                ),
              ],
            ),
          _Phase.done => const Column(
              mainAxisSize: MainAxisSize.min,
              children: [
                Icon(Icons.verified, color: Colors.green, size: 48),
                SizedBox(height: 16),
                Text(
                  'Security is set up. Cross-signing is active and '
                  'your message keys are backed up. Previously '
                  'encrypted messages should now be readable.',
                ),
              ],
            ),
          _Phase.error => Column(
              mainAxisSize: MainAxisSize.min,
              children: [
                const Icon(Icons.error_outline, color: Colors.red, size: 48),
                const SizedBox(height: 16),
                Text(_error ?? 'An unknown error occurred.'),
              ],
            ),
        },
      ),
      actions: switch (_phase) {
        _Phase.checking || _Phase.working || _Phase.restoring => [],
        _Phase.needsRecoveryKey => [
            TextButton(
              onPressed: () => Navigator.of(context).pop(false),
              child: const Text('Cancel'),
            ),
            ElevatedButton(
              onPressed: _unlockWithRecoveryKey,
              child: const Text('Unlock'),
            ),
          ],
        _Phase.showRecoveryKey => [
            TextButton(
              onPressed: () {
                Clipboard.setData(
                    ClipboardData(text: _recoveryKey ?? ''));
                ScaffoldMessenger.of(context).showSnackBar(
                  const SnackBar(
                      content: Text('Recovery key copied to clipboard')),
                );
              },
              child: const Text('Copy'),
            ),
            ElevatedButton(
              onPressed: () async {
                await _restoreKeys();
              },
              child: const Text('I saved it — continue'),
            ),
          ],
        _Phase.done => [
            ElevatedButton(
              onPressed: () => Navigator.of(context).pop(true),
              child: const Text('Done'),
            ),
          ],
        _Phase.error => [
            TextButton(
              onPressed: () {
                setState(() => _phase = _Phase.needsRecoveryKey);
              },
              child: const Text('Try again'),
            ),
            TextButton(
              onPressed: () => Navigator.of(context).pop(false),
              child: const Text('Close'),
            ),
          ],
      },
    );
  }
}
