import { useState, useEffect, useCallback } from 'react';
import { toast } from 'sonner';
import { api } from '@/api';
import { errMessage } from '@/lib/utils';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Card, CardHeader, CardTitle, CardContent } from '@/components/ui/card';
import { FieldRow, Toggle } from './adminShared';

const AUTHENTIK_ICON_URL = '/img/auth.png';

interface OidcForm {
  local_login_enabled: boolean;
  oidc_login_enabled: boolean;
  oidc_provider_name: string;
  oidc_issuer_url: string;
  oidc_client_id: string;
  oidc_client_secret: string;
  oidc_client_secret_clear: boolean;
  oidc_token_auth_method: string;
  oidc_redirect_uri: string;
  oidc_scopes: string;
  oidc_auto_provision: boolean;
  oidc_admin_group: string;
  oidc_default_role: string;
}

interface AuthConfigData {
  local_login_enabled?: boolean;
  oidc_login_enabled?: boolean;
  oidc_provider_name?: string;
  oidc_issuer_url?: string;
  oidc_client_id?: string;
  oidc_token_auth_method?: string;
  oidc_redirect_uri?: string;
  oidc_scopes?: string;
  oidc_auto_provision?: boolean;
  oidc_admin_group?: string;
  oidc_default_role?: string;
  oidc_client_secret_set?: boolean;
  oidc_configured?: boolean;
  oidc_env_fallback_used?: boolean;
  warnings?: string[];
}

interface OidcTestResult {
  ok?: boolean;
  error?: string;
  issuer?: string;
}

function defaultOidcRedirectUri(): string {
  if (typeof window === 'undefined') return '';
  return `${window.location.origin}/api/auth/oidc/callback`;
}

function looksLikeOidcEndpoint(url: string | null | undefined): boolean {
  const value = String(url || '').toLowerCase();
  return /\/(?:authorize|token|userinfo|jwks|certs)\/?$/.test(value);
}

function formFromData(d: AuthConfigData): OidcForm {
  return {
    local_login_enabled: d.local_login_enabled !== false,
    oidc_login_enabled:  !!d.oidc_login_enabled,
    oidc_provider_name:  d.oidc_provider_name || 'authentik',
    oidc_issuer_url:     d.oidc_issuer_url || '',
    oidc_client_id:      d.oidc_client_id || '',
    oidc_client_secret:  '',
    oidc_client_secret_clear: false,
    oidc_token_auth_method: d.oidc_token_auth_method || 'client_secret_basic',
    oidc_redirect_uri:   d.oidc_redirect_uri || defaultOidcRedirectUri(),
    oidc_scopes:         d.oidc_scopes || 'openid email profile groups',
    oidc_auto_provision: d.oidc_auto_provision !== false,
    oidc_admin_group:    d.oidc_admin_group || '',
    oidc_default_role:   d.oidc_default_role || 'user',
  };
}

export default function AuthMethodsCard() {
  const [data, setData]       = useState<AuthConfigData | null>(null);
  const [form, setForm]       = useState<OidcForm | null>(null);
  const [loading, setLoading] = useState(true);
  const [saving, setSaving]   = useState(false);
  const [testingOidc, setTestingOidc] = useState(false);
  const [oidcTest, setOidcTest]       = useState<OidcTestResult | null>(null);

  const load = useCallback(async () => {
    try {
      const d = await api.authModeConfig() as AuthConfigData;
      setData(d);
      setForm(formFromData(d));
    } catch (err) {
      toast.error(errMessage(err, 'Failed to load auth settings.'));
    } finally {
      setLoading(false);
    }
  }, []);

  useEffect(() => { load(); }, [load]);

  const set = (k: keyof OidcForm, v: string | boolean) => setForm(prev => prev ? ({ ...prev, [k]: v }) as OidcForm : prev);

  async function handleSave() {
    setSaving(true);
    try {
      const d = await api.setAuthMode(form) as AuthConfigData;
      setData(d);
      setForm(formFromData(d));
      toast.success('Auth method settings saved.');
    } catch (err) {
      toast.error(errMessage(err, 'Failed to save auth method settings.'));
    } finally {
      setSaving(false);
    }
  }

  async function handleTestOidc() {
    setTestingOidc(true);
    setOidcTest(null);
    try {
      const result = await api.testOidcConfig(form) as OidcTestResult;
      setOidcTest(result);
      toast.success('authentik configuration test passed.');
    } catch (err) {
      const e = err as { data?: OidcTestResult; message?: string };
      const result = e.data || { ok: false, error: e.message || 'OIDC configuration test failed.' };
      setOidcTest(result);
      toast.error(result.error || 'OIDC configuration test failed.');
    } finally {
      setTestingOidc(false);
    }
  }

  if (loading || !form) {
    return (
      <Card>
        <CardContent className="py-8 text-center text-muted-foreground text-sm">
          Loading auth settings…
        </CardContent>
      </Card>
    );
  }

  const secretAvailable = form.oidc_client_secret.trim()
    ? true
    : form.oidc_client_secret_clear
    ? false
    : !!data?.oidc_client_secret_set;
  const oidcConfigured = !!(
    form.oidc_issuer_url.trim() &&
    form.oidc_client_id.trim() &&
    secretAvailable &&
    form.oidc_redirect_uri.trim()
  );
  const adminGroupConfigured    = !!form.oidc_admin_group.trim();
  const wouldLockOut            = !form.local_login_enabled && !form.oidc_login_enabled;
  const cantDisableLocal        = !form.local_login_enabled && (!oidcConfigured || !form.oidc_login_enabled || !adminGroupConfigured);
  const oidcEnabledButIncomplete = form.oidc_login_enabled && !oidcConfigured;
  const canSave                 = !wouldLockOut && !cantDisableLocal && !oidcEnabledButIncomplete && !saving;
  const canTestOidc             = oidcConfigured && !testingOidc;
  const missingFields = [
    !form.oidc_issuer_url.trim() && 'Issuer URL',
    !form.oidc_client_id.trim() && 'Client ID',
    !secretAvailable && 'Client Secret',
    !form.oidc_redirect_uri.trim() && 'Redirect URI',
  ].filter(Boolean);
  const issuerEndpointWarning = looksLikeOidcEndpoint(form.oidc_issuer_url);

  return (
    <Card>
      <CardHeader className="pb-4">
        <div className="flex items-center justify-between gap-3">
          <div>
            <CardTitle>Authentication Methods</CardTitle>
            <p className="text-sm text-muted-foreground mt-1">
              Control local login and authentik/OIDC. Settings are saved in the database;
              environment variables only fill blank fields as bootstrap defaults.
            </p>
          </div>
        </div>
      </CardHeader>

      <CardContent className="space-y-5">

        {((data?.warnings?.length ?? 0) > 0 || wouldLockOut || cantDisableLocal) && (
          <div className="rounded-lg border border-amber-500/25 bg-amber-500/10 px-4 py-3 space-y-1">
            {wouldLockOut && (
              <p className="text-sm text-amber-600 dark:text-amber-400">
                Cannot disable all login methods; at least one must remain enabled.
              </p>
            )}
            {cantDisableLocal && !wouldLockOut && (
              <p className="text-sm text-amber-600 dark:text-amber-400">
                Cannot disable local login without authentik/OIDC configured, enabled, and mapped to an admin group.
              </p>
            )}
            {oidcEnabledButIncomplete && (
              <p className="text-sm text-amber-600 dark:text-amber-400">
                authentik/OIDC needs {missingFields.join(', ')} before it can be enabled.
              </p>
            )}
            {data?.warnings?.map((w, i) => (
              <p key={i} className="text-sm text-amber-600 dark:text-amber-400">{w}</p>
            ))}
          </div>
        )}

        <FieldRow label="Local username/password login">
          <div className="flex items-center gap-3">
            <Toggle checked={form.local_login_enabled} onChange={v => set('local_login_enabled', v)} label="Enable local login" />
            <span className="text-xs text-muted-foreground">{form.local_login_enabled ? 'Enabled' : 'Disabled'}</span>
          </div>
        </FieldRow>

        <FieldRow label="authentik / OIDC login">
          <div className="flex items-center gap-3">
            <Toggle checked={form.oidc_login_enabled} onChange={v => set('oidc_login_enabled', v)} label="Enable OIDC login" />
            <span className={`text-xs ${oidcConfigured ? 'text-muted-foreground' : 'text-amber-500'}`}>
              {!oidcConfigured ? 'Not fully configured' : form.oidc_login_enabled ? 'Enabled' : 'Disabled'}
            </span>
          </div>
        </FieldRow>

        <div className="space-y-4 pt-2 border-t border-border">
          <div className="flex items-center gap-2 pt-1 text-sm font-medium text-muted-foreground">
            <img src={AUTHENTIK_ICON_URL} alt="" aria-hidden="true" className="h-5 w-5 shrink-0 object-contain" />
            <span>authentik / OIDC configuration</span>
          </div>

          <FieldRow label="Provider name">
            <Input value={form.oidc_provider_name} onChange={e => set('oidc_provider_name', e.target.value)} placeholder="authentik" className="max-w-xs h-8 text-sm" />
          </FieldRow>

          <FieldRow label="Issuer / discovery URL">
            <div className="space-y-1">
              <Input
                value={form.oidc_issuer_url}
                onChange={e => set('oidc_issuer_url', e.target.value)}
                placeholder="https://yourURL.com/application/o/bills/.well-known/openid-configuration"
                className="max-w-xl h-8 text-sm"
              />
              <p className={issuerEndpointWarning ? 'text-xs text-amber-500' : 'text-xs text-muted-foreground'}>
                Use the authentik provider issuer URL or full discovery URL, for example https://yourURL.com/application/o/bills/.well-known/openid-configuration.
              </p>
              {issuerEndpointWarning && (
                <p className="text-xs text-amber-500">
                  This looks like an authorization endpoint. In authentik, copy the provider issuer or OpenID Configuration URL.
                </p>
              )}
            </div>
          </FieldRow>

          <FieldRow label="Client ID">
            <Input value={form.oidc_client_id} onChange={e => set('oidc_client_id', e.target.value)} placeholder="authentik client ID" className="max-w-xl h-8 text-sm" />
          </FieldRow>

          <FieldRow label="Client Secret">
            <div className="space-y-2">
              <div className="flex max-w-xl items-center gap-3">
                <Input
                  type="password"
                  value={form.oidc_client_secret}
                  onChange={e => setForm(prev => prev ? ({
                    ...prev,
                    oidc_client_secret: e.target.value,
                    oidc_client_secret_clear: e.target.value ? false : prev.oidc_client_secret_clear,
                  }) : prev)}
                  placeholder="Leave blank to keep existing secret"
                  className="h-8 text-sm"
                />
                <span className={`shrink-0 text-xs ${data?.oidc_client_secret_set && !form.oidc_client_secret_clear ? 'text-emerald-500' : 'text-muted-foreground'}`}>
                  {data?.oidc_client_secret_set && !form.oidc_client_secret_clear ? 'Secret is set' : 'No secret saved'}
                </span>
              </div>
              <label className="flex items-center gap-2 text-xs text-muted-foreground">
                <input type="checkbox" checked={form.oidc_client_secret_clear} onChange={e => set('oidc_client_secret_clear', e.target.checked)} />
                Clear saved secret on save
              </label>
            </div>
          </FieldRow>

          <FieldRow label="Client auth method">
            <div className="space-y-1">
              <select
                value={form.oidc_token_auth_method}
                onChange={e => set('oidc_token_auth_method', e.target.value)}
                className="h-8 rounded-md border border-input bg-background px-3 text-sm"
              >
                <option value="client_secret_basic">client_secret_basic</option>
                <option value="client_secret_post">client_secret_post</option>
              </select>
              <p className="text-xs text-muted-foreground">
                Advanced. Keep client_secret_basic unless your authentik provider explicitly requires client_secret_post.
              </p>
            </div>
          </FieldRow>

          <FieldRow label="Redirect URI">
            <div className="space-y-1">
              <div className="flex max-w-xl gap-2">
                <Input value={form.oidc_redirect_uri} onChange={e => set('oidc_redirect_uri', e.target.value)} placeholder={defaultOidcRedirectUri()} className="h-8 text-sm" />
                <Button type="button" variant="outline" size="sm" onClick={() => set('oidc_redirect_uri', defaultOidcRedirectUri())}>Use Current</Button>
              </div>
              <p className="text-xs text-muted-foreground">Add this exact URL to the Redirect URIs allowed by authentik.</p>
            </div>
          </FieldRow>

          <FieldRow label="Scopes">
            <Input value={form.oidc_scopes} onChange={e => set('oidc_scopes', e.target.value)} placeholder="openid email profile groups" className="max-w-xl h-8 text-sm" />
          </FieldRow>

          <FieldRow label="Admin group">
            <div className="space-y-1">
              <Input value={form.oidc_admin_group} onChange={e => set('oidc_admin_group', e.target.value)} placeholder="e.g. bill-tracker-admins" className="max-w-sm h-8 text-sm" />
              <p className="text-xs text-muted-foreground">Only users in this authentik group become app admins. Admin is never granted by default.</p>
            </div>
          </FieldRow>

          <FieldRow label="Auto-provision users">
            <div className="space-y-1">
              <div className="flex items-center gap-3">
                <Toggle checked={form.oidc_auto_provision} onChange={v => set('oidc_auto_provision', v)} label="Auto-provision users" />
                <span className="text-xs text-muted-foreground">{form.oidc_auto_provision ? 'Enabled' : 'Disabled'}</span>
              </div>
              <p className="text-xs text-muted-foreground">When enabled, valid authentik users are created in this app on first login.</p>
            </div>
          </FieldRow>

          <FieldRow label="Default role">
            <div className="flex items-center gap-2">
              <Input value="user" readOnly className="max-w-[120px] h-8 text-sm" />
              <span className="text-xs text-muted-foreground">Admin role only via admin group.</span>
            </div>
          </FieldRow>

          {data?.oidc_env_fallback_used && (
            <div className="rounded-lg border border-sky-500/25 bg-sky-500/10 px-4 py-3 text-xs text-sky-700 dark:text-sky-400">
              One or more blank database fields are currently using environment fallback values. Saving values here takes precedence.
            </div>
          )}

          {oidcTest && (
            <div className={`rounded-lg border px-4 py-3 text-xs ${
              oidcTest.ok
                ? 'border-emerald-500/25 bg-emerald-500/10 text-emerald-700 dark:text-emerald-400'
                : 'border-destructive/25 bg-destructive/10 text-destructive'
            }`}>
              {oidcTest.ok
                ? `Configuration test passed for ${oidcTest.issuer || form.oidc_issuer_url}.`
                : oidcTest.error || 'Configuration test failed.'}
            </div>
          )}
        </div>

        <div className="pt-2 border-t border-border flex flex-wrap items-center gap-2">
          <Button variant="outline" onClick={handleTestOidc} disabled={!canTestOidc}>
            {testingOidc ? 'Testing…' : 'Test Configuration'}
          </Button>
          <Button
            variant="outline"
            disabled={!data?.oidc_login_enabled || !data?.oidc_configured}
            onClick={() => { window.location.href = '/api/auth/oidc/login?redirect_to=/admin'; }}
          >
            <img src={AUTHENTIK_ICON_URL} alt="" aria-hidden="true" className="mr-2 h-4 w-4 shrink-0 object-contain" />
            Test authentik Login
          </Button>
          <Button onClick={handleSave} disabled={!canSave}>
            {saving ? 'Saving…' : 'Save Auth Settings'}
          </Button>
        </div>

      </CardContent>
    </Card>
  );
}
