import React, { useState, useEffect } from 'react';
import { useNavigate, useLocation } from 'react-router-dom';
import { Wifi, WifiOff, Eye, EyeOff, Server, Globe } from 'lucide-react';
import { useAuthStore } from '@/store/authStore';
import type { CustomHeaderEntry, CustomHeadersApplyTo, ServerProfile } from '@/store/authStoreTypes';
import { pingWithCredentialsForProfile, scheduleInstantMixProbeForServer } from '@/lib/api/subsonic';
import { CustomHttpHeadersEditor } from '@/features/settings';
import {
  DEFAULT_CUSTOM_HEADERS_APPLY_TO,
  serverCustomHeadersFromForm,
  validateCustomHeaders,
} from '@/lib/server/serverHttpHeaders';
import { syncServerHttpContextForProfile } from '@/lib/server/syncServerHttpContext';
import { useTranslation } from 'react-i18next';
import i18n from '@/lib/i18n';
import CustomSelect from '@/ui/CustomSelect';
import {
  decodeServerMagicString,
  DECODED_PASSWORD_VISUAL_MASK,
  encodeServerMagicString,
  type ServerMagicPayload,
} from '@/lib/server/serverMagicString';
import { shortHostFromServerUrl, serverListDisplayLabel } from '@/lib/server/serverDisplayName';

const PsysonicLogo = () => (
  <img src="/logo-psysonic.png" width="64" height="64" alt="Psysonic" style={{ borderRadius: 18 }} />
);

export default function Login() {
  const navigate = useNavigate();
  const location = useLocation();
  const { t } = useTranslation();
  const { addServer, updateServer, setActiveServer, setLoggedIn, setConnecting, setConnectionError, servers } = useAuthStore();

  // alternateUrl / shareUsesLocalUrl are not user-editable on this page (Login
  // stays single-address by design); they're populated only when a v2 magic
  // string is decoded so the dual-address shape persists straight from the
  // invite onto the saved profile.
  const [form, setForm] = useState({
    serverName: '',
    url: '',
    username: '',
    password: '',
    alternateUrl: '' as string,
    shareUsesLocalUrl: false,
    customHeaders: [{ name: '', value: '' }] as CustomHeaderEntry[],
    customHeadersApplyTo: DEFAULT_CUSTOM_HEADERS_APPLY_TO as CustomHeadersApplyTo,
    customHeadersOpen: false,
  });
  const [magicString, setMagicString] = useState('');
  const [showPass, setShowPass] = useState(false);
  /** After a valid magic string decode, do not allow revealing the password in the UI. */
  const [blockPasswordReveal, setBlockPasswordReveal] = useState(false);
  const [status, setStatus] = useState<'idle' | 'testing' | 'ok' | 'error'>('idle');
  const [testMessage, setTestMessage] = useState('');

  useEffect(() => {
    const inv = (location.state as { openAddServerInvite?: ServerMagicPayload } | null)?.openAddServerInvite;
    if (!inv) return;
    // React Compiler set-state-in-effect rule: local state synced with store/prop inputs when the effect’s dependencies change.
    // eslint-disable-next-line react-hooks/set-state-in-effect
    setShowPass(false);
    setBlockPasswordReveal(true);
    setForm(f => ({
      ...f,
      serverName: (inv.name && inv.name.trim()) || shortHostFromServerUrl(inv.url),
      url: inv.url,
      username: inv.username,
      password: inv.password,
      alternateUrl: inv.alternateUrl ?? '',
      shareUsesLocalUrl: inv.shareUsesLocalUrl ?? false,
    }));
    setMagicString(encodeServerMagicString(inv));
    navigate('/login', { replace: true, state: {} });
  }, [location.state, navigate]);

  const update = (k: keyof typeof form) => (e: React.ChangeEvent<HTMLInputElement>) =>
    setForm(f => ({ ...f, [k]: e.target.value }));

  const handleMagicStringChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const v = e.target.value;
    setMagicString(v);
    const trimmed = v.trim();
    const decoded = decodeServerMagicString(trimmed);
    if (decoded) {
      setShowPass(false);
      setBlockPasswordReveal(true);
      setForm(f => ({
        ...f,
        serverName: (decoded.name && decoded.name.trim()) || shortHostFromServerUrl(decoded.url),
        url: decoded.url,
        username: decoded.username,
        password: decoded.password,
        alternateUrl: decoded.alternateUrl ?? '',
        shareUsesLocalUrl: decoded.shareUsesLocalUrl ?? false,
      }));
      if (status === 'error') {
        setStatus('idle');
        setTestMessage('');
      }
    }
  };

  const attemptConnect = async (profile: {
    name: string;
    url: string;
    username: string;
    password: string;
    alternateUrl?: string;
    shareUsesLocalUrl?: boolean;
    customHeaders?: CustomHeaderEntry[];
    customHeadersApplyTo?: CustomHeadersApplyTo;
  }) => {
    if (!profile.url.trim()) {
      setTestMessage(t('login.urlRequired'));
      setStatus('error');
      return;
    }

    const headerRows = (profile.customHeaders ?? []).filter(h => h.name.trim() || h.value);
    if (headerRows.length) {
      const headerValidation = validateCustomHeaders(headerRows);
      if (!headerValidation.ok) {
        const first = headerValidation.fieldErrors[0]!;
        setTestMessage(t(first.messageKey, { defaultValue: first.messageKey }));
        setStatus('error');
        return;
      }
    }

    setStatus('testing');
    setTestMessage(t('login.connecting'));
    setConnecting(true);
    setConnectionError(null);

    const urlTrimmed = profile.url.trim();
    const usernameTrimmed = profile.username.trim();
    const headersPayload = serverCustomHeadersFromForm(
      profile.customHeaders ?? [],
      profile.customHeadersApplyTo ?? DEFAULT_CUSTOM_HEADERS_APPLY_TO,
    );
    const pingProfile: Pick<
      ServerProfile,
      'url' | 'alternateUrl' | 'username' | 'password' | 'customHeaders' | 'customHeadersApplyTo'
    > = {
      url: urlTrimmed,
      username: usernameTrimmed,
      password: profile.password,
      alternateUrl: profile.alternateUrl?.trim() || undefined,
      ...headersPayload,
    };

    let ping: Awaited<ReturnType<typeof pingWithCredentialsForProfile>>;
    try {
      ping = await pingWithCredentialsForProfile(pingProfile, urlTrimmed);
    } catch {
      ping = { ok: false };
    }

    setConnecting(false);

    if (ping.ok) {
      // Connection succeeded — now persist to store
      const existing = servers.find(s => s.url === profile.url.trim() && s.username === profile.username.trim());
      // Dual-address fields persist straight off a v2 magic invite even
      // though Login itself never shows the second-address field. The
      // user can edit/remove them later via Settings → Servers.
      const altTrimmed = profile.alternateUrl?.trim() ?? '';
      const savedHeaders = serverCustomHeadersFromForm(
        profile.customHeaders ?? [],
        profile.customHeadersApplyTo ?? DEFAULT_CUSTOM_HEADERS_APPLY_TO,
      );
      let serverId: string;
      if (existing) {
        updateServer(existing.id, {
          name: profile.name.trim() || profile.url.trim(),
          password: profile.password,
          ...savedHeaders,
          ...(altTrimmed
            ? {
                alternateUrl: altTrimmed,
                shareUsesLocalUrl: profile.shareUsesLocalUrl ?? false,
              }
            : {}),
        });
        serverId = existing.id;
      } else {
        serverId = addServer({
          name: profile.name.trim() || profile.url.trim(),
          url: urlTrimmed,
          username: usernameTrimmed,
          password: profile.password,
          ...savedHeaders,
          ...(altTrimmed
            ? {
                alternateUrl: altTrimmed,
                shareUsesLocalUrl: profile.shareUsesLocalUrl ?? false,
              }
            : {}),
        });
      }
      const identity = {
        type: ping.type,
        serverVersion: ping.serverVersion,
        openSubsonic: ping.openSubsonic,
      };
      useAuthStore.getState().setSubsonicServerIdentity(serverId, identity);
      scheduleInstantMixProbeForServer(
        serverId,
        urlTrimmed,
        usernameTrimmed,
        profile.password,
        identity,
      );
      const saved = useAuthStore.getState().servers.find(s => s.id === serverId);
      if (saved) void syncServerHttpContextForProfile(saved);
      setActiveServer(serverId);
      setLoggedIn(true);
      setStatus('ok');
      setTestMessage(t('login.connected'));
      setTimeout(() => navigate('/'), 600);
    } else {
      setStatus('error');
      setConnectionError(t('login.error'));
      setTestMessage(t('login.error'));
    }
  };

  const handleFormSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    const ms = magicString.trim();
    if (ms) {
      const decoded = decodeServerMagicString(ms);
      if (!decoded) {
        setStatus('error');
        setTestMessage(t('login.magicStringInvalid'));
        return;
      }
      await attemptConnect({
        name: form.serverName.trim() || (decoded.name && decoded.name.trim()) || shortHostFromServerUrl(decoded.url),
        url: decoded.url,
        username: decoded.username,
        password: decoded.password,
        alternateUrl: decoded.alternateUrl,
        shareUsesLocalUrl: decoded.shareUsesLocalUrl,
        customHeaders: form.customHeaders,
        customHeadersApplyTo: form.customHeadersApplyTo,
      });
      return;
    }
    await attemptConnect({
      name: form.serverName,
      url: form.url,
      username: form.username,
      password: form.password,
      alternateUrl: form.alternateUrl,
      shareUsesLocalUrl: form.shareUsesLocalUrl,
      customHeaders: form.customHeaders,
      customHeadersApplyTo: form.customHeadersApplyTo,
    });
  };

  const handleQuickConnect = async (srv: typeof servers[0]) => {
    setMagicString('');
    setBlockPasswordReveal(false);
    setShowPass(false);
    setForm({
      serverName: srv.name,
      url: srv.url,
      username: srv.username,
      password: srv.password,
      alternateUrl: srv.alternateUrl ?? '',
      shareUsesLocalUrl: srv.shareUsesLocalUrl ?? false,
      customHeaders: srv.customHeaders?.length
        ? srv.customHeaders.map(h => ({ ...h }))
        : [{ name: '', value: '' }],
      customHeadersApplyTo: srv.customHeadersApplyTo ?? DEFAULT_CUSTOM_HEADERS_APPLY_TO,
      customHeadersOpen: Boolean(srv.customHeaders?.length),
    });
    await attemptConnect({
      name: srv.name,
      url: srv.url,
      username: srv.username,
      password: srv.password,
      alternateUrl: srv.alternateUrl,
      shareUsesLocalUrl: srv.shareUsesLocalUrl,
      customHeaders: srv.customHeaders,
      customHeadersApplyTo: srv.customHeadersApplyTo,
    });
  };

  return (
    <div className="login-page">
      <div className="login-bg" aria-hidden="true" />
      <div className="login-card animate-fade-in">
        <div className="login-lang-picker" aria-label={t('settings.language')}>
          <Globe size={14} aria-hidden="true" />
          <CustomSelect
            value={i18n.language}
            onChange={v => i18n.changeLanguage(v)}
            options={[
              { value: 'en', label: t('settings.languageEn') },
              { value: 'de', label: t('settings.languageDe') },
              { value: 'es', label: t('settings.languageEs') },
              { value: 'fr', label: t('settings.languageFr') },
              { value: 'it', label: t('settings.languageIt') },
              { value: 'nl', label: t('settings.languageNl') },
              { value: 'nb', label: t('settings.languageNb') },
              { value: 'ru', label: t('settings.languageRu') },
              { value: 'zh', label: t('settings.languageZh') },
              { value: 'ro', label: t('settings.languageRo') },
              { value: 'ja', label: t('settings.languageJa') },
              { value: 'hu', label: t('settings.languageHu') },
              { value: 'pl', label: t('settings.languagePl') },
              { value: 'bg', label: t('settings.languageBg') },
            ]}
          />
        </div>
        <div className="login-logo">
          <PsysonicLogo />
        </div>
        <h1 className="login-title">Psysonic</h1>
        <p className="login-subtitle">{t('login.subtitle')}</p>

        {/* Saved servers quick-connect */}
        {servers.length > 0 && (
          <div className="login-saved-servers">
            <div className="login-saved-label">{t('login.savedServers')}</div>
            {servers.map(srv => (
              <button
                key={srv.id}
                className="btn btn-surface login-server-btn"
                onClick={() => handleQuickConnect(srv)}
                disabled={status === 'testing'}
              >
                <Server size={14} style={{ flexShrink: 0 }} />
                <div style={{ textAlign: 'left', minWidth: 0 }}>
                  <div style={{ fontWeight: 600 }} className="truncate">{serverListDisplayLabel(srv, servers)}</div>
                  <div style={{ fontSize: 11, opacity: 0.7 }} className="truncate">{srv.username}@{srv.url}</div>
                </div>
              </button>
            ))}
            <div className="login-divider"><span>{t('login.addNew')}</span></div>
          </div>
        )}

        <form className="login-form" onSubmit={handleFormSubmit} noValidate>
          <div className="form-group">
            <label htmlFor="login-server-name">{t('login.serverName')}</label>
            <input
              id="login-server-name"
              className="input"
              type="text"
              placeholder={t('login.serverNamePlaceholder')}
              value={form.serverName}
              onChange={update('serverName')}
              autoComplete="off"
            />
          </div>

          <div className="form-group">
            <label htmlFor="login-url">{t('login.serverUrl')}</label>
            <input
              id="login-url"
              className="input"
              type="text"
              placeholder={t('login.serverUrlPlaceholder')}
              value={form.url}
              onChange={update('url')}
              autoComplete="off"
            />
          </div>

          <div className="form-row">
            <div className="form-group">
              <label htmlFor="login-username">{t('login.username')}</label>
              <input
                id="login-username"
                className="input"
                type="text"
                placeholder={t('login.usernamePlaceholder')}
                value={form.username}
                onChange={update('username')}
                readOnly={blockPasswordReveal}
                autoComplete="username"
                style={blockPasswordReveal ? { cursor: 'default' } : undefined}
              />
            </div>
            <div className="form-group">
              <label htmlFor={blockPasswordReveal ? 'login-password-mask' : 'login-password'}>{t('login.password')}</label>
              {blockPasswordReveal ? (
                <input
                  id="login-password-mask"
                  className="input"
                  type="text"
                  readOnly
                  value={DECODED_PASSWORD_VISUAL_MASK}
                  autoComplete="off"
                  aria-label={t('login.password')}
                  style={{ letterSpacing: '0.12em', cursor: 'default' }}
                />
              ) : (
                <div style={{ position: 'relative' }}>
                  <input
                    id="login-password"
                    className="input"
                    type={showPass ? 'text' : 'password'}
                    placeholder="••••••••"
                    value={form.password}
                    onChange={update('password')}
                    autoComplete="current-password"
                    style={{ paddingRight: '2.5rem' }}
                  />
                  <button
                    type="button"
                    style={{ position: 'absolute', right: '10px', top: '50%', transform: 'translateY(-50%)', color: 'var(--text-muted)' }}
                    onClick={() => setShowPass(v => !v)}
                    aria-label={showPass ? t('login.hidePassword') : t('login.showPassword')}
                  >
                    {showPass ? <EyeOff size={16} /> : <Eye size={16} />}
                  </button>
                </div>
              )}
            </div>
          </div>

          <CustomHttpHeadersEditor
            headers={form.customHeaders}
            applyTo={form.customHeadersApplyTo}
            open={form.customHeadersOpen}
            onOpenChange={customHeadersOpen => setForm(f => ({ ...f, customHeadersOpen }))}
            onHeadersChange={customHeaders => setForm(f => ({ ...f, customHeaders }))}
            onApplyToChange={customHeadersApplyTo => setForm(f => ({ ...f, customHeadersApplyTo }))}
            radioGroupName="loginCustomHeadersApplyTo"
          />

          <div className="form-group">
            <label htmlFor="login-magic-string">{t('login.orMagicString')}</label>
            <input
              id="login-magic-string"
              className="input"
              type="text"
              placeholder={t('login.magicStringPlaceholder')}
              value={magicString}
              onChange={handleMagicStringChange}
              autoComplete="off"
            />
          </div>

          {testMessage && (
            <div className={`login-status login-status--${status}`} role="alert">
              {status === 'testing' && <div className="spinner" style={{ width: 16, height: 16, borderWidth: 2 }} />}
              {status === 'ok' && <Wifi size={16} />}
              {status === 'error' && <WifiOff size={16} />}
              <span>{testMessage}</span>
            </div>
          )}

          <button
            type="submit"
            className="btn btn-primary"
            style={{ width: '100%', justifyContent: 'center', padding: '0.75rem', fontSize: '15px' }}
            id="login-connect-btn"
            disabled={status === 'testing'}
          >
            {status === 'testing' ? t('login.connecting') : t('login.connect')}
          </button>
        </form>
      </div>
    </div>
  );
}
