import "package:flutter/material.dart";

import "../api.dart";
import "../biometric.dart";
import "../config.dart";
import "../i18n.dart";
import "../main.dart";
import "../servers.dart";
import "../theme.dart";
import "servers_sheet.dart";

class LoginScreen extends StatefulWidget {
  const LoginScreen({super.key});
  @override
  State<LoginScreen> createState() => _LoginScreenState();
}

class _LoginScreenState extends State<LoginScreen> {
  final _email = TextEditingController();
  final _password = TextEditingController();
  final _server = TextEditingController();
  final _formKey = GlobalKey<FormState>();
  bool _loading = false;
  bool _showPassword = false;
  String? _error;
  String? _serverSaved;

  // Biometric ("Face recognition" / "Fingerprint") sign-in state.
  bool _bioAvailable = false;
  bool _bioEnabled = false;
  bool _hasFace = false;
  bool _hasFingerprint = false;
  String? _bioEmail;
  bool _autoPrompted = false;

  @override
  void initState() {
    super.initState();
    // The login form signs into whichever server is active, so Server settings
    // edits that one's address — on a fresh install, the home server's.
    _server.text = serverRegistry.active.url;
    _initBiometrics();
  }

  Future<void> _initBiometrics() async {
    final available = await biometricAuth.isAvailable();
    final email = await biometricAuth.enabledEmail();
    final face = available && await biometricAuth.hasFace();
    final finger = available && await biometricAuth.hasFingerprint();
    if (!mounted) return;
    setState(() {
      _bioAvailable = available;
      _bioEnabled = email != null;
      _bioEmail = email;
      _hasFace = face;
      _hasFingerprint = finger;
    });
    // If biometric login is set up, offer it immediately (once) on screen load.
    if (_bioEnabled && _bioAvailable && !_autoPrompted) {
      _autoPrompted = true;
      _biometricLogin();
    }
  }

  @override
  void dispose() {
    _email.dispose();
    _password.dispose();
    _server.dispose();
    super.dispose();
  }

  void _saveServer() {
    final saved = serverRegistry.update(serverRegistry.activeId, url: _server.text);
    setState(() {
      _server.text = saved?.url ?? "";
      _serverSaved = t("login.savedNote");
    });
  }

  void _resetServer() {
    serverRegistry.update(serverRegistry.activeId, url: "");
    setState(() {
      _server.clear();
      _serverSaved = t("login.resetNote");
    });
  }

  Future<void> _submit() async {
    if (!_formKey.currentState!.validate()) return;
    setState(() {
      _loading = true;
      _error = null;
    });
    final email = _email.text.trim();
    final password = _password.text;
    try {
      await authService.login(email, password);
      // Pull the full profile so saved appearance prefs (theme/font/date) apply.
      try {
        appSettings.applyFromProfile(await apiClient.getMe());
      } catch (_) {}
      // Offer to remember these (known-good) credentials for biometric login.
      await _maybeOfferBiometricEnrollment(email, password);
      // The app root rebuilds to the dashboard via the auth listener.
    } catch (e) {
      setState(() => _error = e.toString());
    } finally {
      if (mounted) setState(() => _loading = false);
    }
  }

  /// After a successful password login, if the device supports biometrics and
  /// they aren't set up yet, ask whether to enable biometric sign-in.
  Future<void> _maybeOfferBiometricEnrollment(String email, String password) async {
    if (!_bioAvailable || _bioEnabled || !mounted) return;
    final method = _hasFace && !_hasFingerprint
        ? t("login.methodFace")
        : _hasFingerprint && !_hasFace
            ? t("login.methodFingerprint")
            : t("login.methodBiometrics");
    final ok = await showDialog<bool>(
      context: context,
      builder: (ctx) => AlertDialog(
        title: Text(t("login.enrollTitle")),
        content: Text(t("login.enrollBody", params: {"method": method})),
        actions: [
          TextButton(onPressed: () => Navigator.pop(ctx, false), child: Text(t("login.enrollNotNow"))),
          FilledButton(onPressed: () => Navigator.pop(ctx, true), child: Text(t("login.enrollEnable"))),
        ],
      ),
    );
    if (ok == true) {
      await biometricAuth.enable(email, password);
    }
  }

  Future<void> _biometricLogin() async {
    setState(() {
      _loading = true;
      _error = null;
    });
    try {
      final creds = await biometricAuth.unlock(
        reason: _bioEmail != null
            ? t("login.bioReasonSignInAs", params: {"email": _bioEmail})
            : t("login.bioReasonSignIn"),
      );
      if (creds == null) {
        // User cancelled the prompt, or nothing is stored.
        if (mounted) setState(() => _loading = false);
        return;
      }
      await authService.login(creds.$1, creds.$2);
      try {
        appSettings.applyFromProfile(await apiClient.getMe());
      } catch (_) {}
    } on BiometricException catch (e) {
      if (mounted) setState(() => _error = e.message);
    } on ApiException catch (e) {
      // Stored credentials no longer work (e.g. password changed) — turn off
      // biometric login and fall back to the password form.
      if (e.status == 400 || e.status == 401) {
        await biometricAuth.disable();
        if (mounted) {
          setState(() {
            _bioEnabled = false;
            _error = t("login.savedInvalid");
          });
        }
      } else if (mounted) {
        setState(() => _error = e.message);
      }
    } catch (e) {
      if (mounted) setState(() => _error = e.toString());
    } finally {
      if (mounted) setState(() => _loading = false);
    }
  }

  /// Biometric sign-in buttons, shown only once biometric login is set up on
  /// this device. Labels reflect the enrolled biometric kind(s).
  Widget _buildBiometricSection() {
    if (!_bioEnabled || !_bioAvailable) return const SizedBox.shrink();

    final buttons = <Widget>[];
    Widget bioButton(IconData icon, String label) => Padding(
          padding: const EdgeInsets.only(top: 8),
          child: SizedBox(
            width: double.infinity,
            child: OutlinedButton.icon(
              onPressed: _loading ? null : _biometricLogin,
              icon: Icon(icon),
              label: Padding(
                padding: const EdgeInsets.symmetric(vertical: 8),
                child: Text(label),
              ),
            ),
          ),
        );

    if (_hasFace) buttons.add(bioButton(Icons.face, t("login.signInWithFace")));
    if (_hasFingerprint) buttons.add(bioButton(Icons.fingerprint, t("login.signInWithFingerprint")));
    if (buttons.isEmpty) buttons.add(bioButton(Icons.lock_outline, t("login.signInWithBiometrics")));

    return Column(
      children: [
        Padding(
          padding: const EdgeInsets.symmetric(vertical: 12),
          child: Row(
            children: [
              const Expanded(child: Divider()),
              Padding(
                padding: const EdgeInsets.symmetric(horizontal: 8),
                child: Text(t("login.or"), style: const TextStyle(color: Colors.grey, fontSize: 12)),
              ),
              const Expanded(child: Divider()),
            ],
          ),
        ),
        ...buttons,
      ],
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: SingleChildScrollView(
          padding: const EdgeInsets.all(24),
          child: ConstrainedBox(
            constraints: const BoxConstraints(maxWidth: 380),
            child: Column(
              mainAxisSize: MainAxisSize.min,
              children: [
                const DriverVaultLogo(markSize: 40, fontSize: 30),
                const SizedBox(height: 10),
                Text(t("login.tagline"),
                    style: TextStyle(color: DriverVault.muted(context))),
                const SizedBox(height: 24),
                Form(
                  key: _formKey,
                  child: Column(
                    children: [
                      if (_error != null)
                        Container(
                          width: double.infinity,
                          margin: const EdgeInsets.only(bottom: 12),
                          padding: const EdgeInsets.all(10),
                          decoration: BoxDecoration(
                            color: Theme.of(context).colorScheme.errorContainer,
                            borderRadius: BorderRadius.circular(DriverVault.radiusControl),
                          ),
                          child: Text(_error!, style: const TextStyle(color: DriverVault.danger)),
                        ),
                      TextFormField(
                        controller: _email,
                        keyboardType: TextInputType.emailAddress,
                        decoration: InputDecoration(labelText: t("login.email"), border: const OutlineInputBorder()),
                        validator: (v) => (v == null || v.isEmpty) ? t("common.required") : null,
                      ),
                      const SizedBox(height: 12),
                      TextFormField(
                        controller: _password,
                        obscureText: !_showPassword,
                        decoration: InputDecoration(
                          labelText: t("login.password"),
                          border: const OutlineInputBorder(),
                          suffixIcon: IconButton(
                            icon: Icon(_showPassword ? Icons.visibility_off : Icons.visibility),
                            tooltip: _showPassword ? t("login.hidePassword") : t("login.showPassword"),
                            onPressed: () => setState(() => _showPassword = !_showPassword),
                          ),
                        ),
                        validator: (v) => (v == null || v.isEmpty) ? t("common.required") : null,
                        onFieldSubmitted: (_) => _submit(),
                      ),
                      const SizedBox(height: 16),
                      SizedBox(
                        width: double.infinity,
                        child: FilledButton(
                          onPressed: _loading ? null : _submit,
                          child: Padding(
                            padding: const EdgeInsets.symmetric(vertical: 12),
                            child: Text(_loading ? t("login.submitting") : t("login.submit")),
                          ),
                        ),
                      ),
                    ],
                  ),
                ),
                _buildBiometricSection(),
                const SizedBox(height: 8),
                _ServerSettings(
                  controller: _server,
                  savedNote: _serverSaved,
                  onSave: _saveServer,
                  onReset: _resetServer,
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

/// Collapsible "Server settings" on the login screen: the address of the server
/// this form signs into (blank = the compile-time default), plus the way back to
/// the others once more than one has been added. Mirrors the web app.
class _ServerSettings extends StatefulWidget {
  final TextEditingController controller;
  final String? savedNote;
  final VoidCallback onSave;
  final VoidCallback onReset;
  const _ServerSettings({
    required this.controller,
    required this.savedNote,
    required this.onSave,
    required this.onReset,
  });
  @override
  State<_ServerSettings> createState() => _ServerSettingsState();
}

class _ServerSettingsState extends State<_ServerSettings> {
  bool _open = false;

  @override
  Widget build(BuildContext context) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        InkWell(
          onTap: () => setState(() => _open = !_open),
          child: Padding(
            padding: const EdgeInsets.symmetric(vertical: 8),
            child: Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: [
                Text(
                    serverRegistry.list.length > 1
                        ? "${t("login.serverSettings")} · ${serverRegistry.displayName(serverRegistry.active)}"
                        : t("login.serverSettings"),
                    style: const TextStyle(fontSize: 12, fontWeight: FontWeight.w600, color: Colors.grey)),
                Icon(_open ? Icons.expand_less : Icons.expand_more, size: 18, color: Colors.grey),
              ],
            ),
          ),
        ),
        if (_open) ...[
          TextField(
            controller: widget.controller,
            keyboardType: TextInputType.url,
            autocorrect: false,
            decoration: InputDecoration(
              labelText: t("login.apiServerUrl"),
              hintText: kDefaultApiBase,
              border: const OutlineInputBorder(),
              isDense: true,
            ),
          ),
          Padding(
            padding: const EdgeInsets.only(top: 4),
            child: Text(t("login.leaveBlank"),
                style: const TextStyle(color: Colors.grey, fontSize: 11)),
          ),
          const SizedBox(height: 4),
          Row(
            children: [
              OutlinedButton(onPressed: widget.onSave, child: Text(t("common.save"))),
              const SizedBox(width: 8),
              TextButton(onPressed: widget.onReset, child: Text(t("login.reset"))),
              if (widget.savedNote != null)
                Padding(
                  padding: const EdgeInsets.only(left: 4),
                  child: Text(widget.savedNote!,
                      style: const TextStyle(color: DriverVault.success, fontSize: 12)),
                ),
            ],
          ),
          // A session can expire on a server that isn't home, which lands here
          // with that one still active — so the picker has to be reachable
          // before signing in, not only from inside the app.
          if (serverRegistry.list.length > 1)
            Align(
              alignment: Alignment.centerLeft,
              child: TextButton.icon(
                onPressed: () async {
                  await showServerPicker(context);
                  if (!mounted) return;
                  setState(() => widget.controller.text = serverRegistry.active.url);
                },
                icon: const Icon(Icons.dns_outlined, size: 18),
                label: Text(t("servers.title")),
              ),
            ),
        ],
      ],
    );
  }
}
