'use client';

import { useState, useEffect, useMemo, useRef } from 'react';
import { useRouter } from 'next/navigation';
import { Save, KeyRound, Globe, Upload } from 'lucide-react';

import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
import { WarningCallout } from '@/components/ui/warning-callout';
import { PageHeader } from '@/components/shared/page-header';
import { CountryCombobox } from '@/components/shared/country-combobox';
import { PhoneInput, type PhoneInputValue } from '@/components/shared/phone-input';
import { TimezoneCombobox } from '@/components/shared/timezone-combobox';
import { ImageCropperDialog } from '@/components/shared/image-cropper-dialog';
import { NotificationPreferencesForm } from '@/components/notifications/notification-preferences-form';
import { ReminderDigestForm } from '@/components/notifications/reminder-digest-form';
import { apiFetch } from '@/lib/api/client';
import { primaryTimezoneFor } from '@/lib/i18n/timezones';
import type { CountryCode } from '@/lib/i18n/countries';

interface MeResponse {
  user?: { name: string; email: string };
  preferences?: { country?: string; timezone?: string; digestTimeOfDay?: string };
  profile?: {
    avatarFileId?: string | null;
    firstName?: string | null;
    lastName?: string | null;
    displayName?: string | null;
    username?: string | null;
  };
}

export function UserSettings() {
  const router = useRouter();
  const [firstName, setFirstName] = useState('');
  const [lastName, setLastName] = useState('');
  const [displayName, setDisplayName] = useState('');
  const [username, setUsername] = useState('');
  const [originalUsername, setOriginalUsername] = useState('');
  const [usernameMsg, setUsernameMsg] = useState<string | null>(null);
  const [phone, setPhone] = useState('');
  const [email, setEmail] = useState('');
  const [originalEmail, setOriginalEmail] = useState('');
  const [country, setCountry] = useState<string | null>(null);
  const [timezone, setTimezone] = useState<string | null>(null);
  /** Phase 4 - default reminder firing time-of-day (HH:MM). Drives the
   *  `<ReminderForm>` "Due Date & Time" default when the rep creates a
   *  reminder without explicitly picking a time. */
  const [digestTimeOfDay, setDigestTimeOfDay] = useState<string>('09:00');
  const [saving, setSaving] = useState<string | null>(null);
  const [message, setMessage] = useState<string | null>(null);
  const [resetMsg, setResetMsg] = useState<string | null>(null);
  const [emailMsg, setEmailMsg] = useState<string | null>(null);
  const [avatarFileId, setAvatarFileId] = useState<string | null>(null);
  const [avatarUrl, setAvatarUrl] = useState<string | null>(null);
  const [pendingAvatarFile, setPendingAvatarFile] = useState<File | null>(null);
  const [cropperOpen, setCropperOpen] = useState(false);
  const fileInputRef = useRef<HTMLInputElement>(null);

  // Browser-detected timezone - surfaces a banner when the rep is
  // travelling and the saved value no longer matches their device's
  // locale. Quietly absent in SSR; the picker still works without it.
  const detectedTz = useMemo(() => {
    if (typeof Intl === 'undefined') return null;
    try {
      return Intl.DateTimeFormat().resolvedOptions().timeZone;
    } catch {
      return null;
    }
  }, []);

  useEffect(() => {
    async function loadProfile() {
      const res = await apiFetch<{ data: MeResponse }>('/api/v1/me', { method: 'GET' });
      setFirstName(res.data.profile?.firstName ?? '');
      setLastName(res.data.profile?.lastName ?? '');
      // Display name is the override; fall back to user.name if profile
      // doesn't carry one (e.g. legacy rows pre-Wave 10).
      setDisplayName(res.data.profile?.displayName ?? res.data.user?.name ?? '');
      setEmail(res.data.user?.email ?? '');
      setOriginalEmail(res.data.user?.email ?? '');
      setUsername(res.data.profile?.username ?? '');
      setOriginalUsername(res.data.profile?.username ?? '');
      setCountry(res.data.preferences?.country ?? null);
      // Fall back to the browser-detected zone when no value has been
      // saved yet - first-time users land on a sensible default rather
      // than an empty picker. Doesn't overwrite an explicit choice.
      setTimezone(res.data.preferences?.timezone ?? detectedTz ?? null);
      setDigestTimeOfDay(res.data.preferences?.digestTimeOfDay ?? '09:00');
      const fid = res.data.profile?.avatarFileId ?? null;
      setAvatarFileId(fid);
      setAvatarUrl(fid ? `/api/v1/files/${fid}/preview` : null);
    }
    void loadProfile();
  }, [detectedTz]);

  // When the user picks a country and no timezone is set, suggest the
  // primary zone for that country. setState in effect is intentional -
  // we're reacting to a discrete user choice (country picker).
  useEffect(() => {
    if (!country || timezone) return;
    const primary = primaryTimezoneFor(country as CountryCode);
    // eslint-disable-next-line react-hooks/set-state-in-effect
    if (primary) setTimezone(primary);
  }, [country, timezone]);

  function handleAvatarPicked(e: React.ChangeEvent<HTMLInputElement>) {
    const file = e.target.files?.[0] ?? null;
    if (!file) return;
    setPendingAvatarFile(file);
    setCropperOpen(true);
    // Reset so picking the same file twice still triggers onChange.
    if (fileInputRef.current) fileInputRef.current.value = '';
  }

  async function uploadAvatar(blob: Blob) {
    const fd = new FormData();
    // Preserve the cropper's chosen format - a transparent PNG uploaded
    // as JPEG composites the alpha against black on export.
    const mime = blob.type || 'image/jpeg';
    const ext = mime === 'image/png' ? 'png' : 'jpg';
    fd.append('file', new File([blob], `avatar.${ext}`, { type: mime }));
    const res = await fetch('/api/v1/me/avatar', { method: 'POST', body: fd });
    if (!res.ok) throw new Error('Avatar upload failed');
    const json = (await res.json()) as { data: { avatarFileId: string } };
    setAvatarFileId(json.data.avatarFileId);
    // Cache-bust the preview URL with a query so the new image renders.
    setAvatarUrl(`/api/v1/files/${json.data.avatarFileId}/preview?t=${Date.now()}`);
  }

  function handleCountryChange(iso: string | null) {
    setCountry(iso);
    // Auto-default timezone when the rep picks a country and hasn't
    // explicitly set a timezone yet. Same trick as the client overview.
    if (iso && !timezone) {
      const tz = primaryTimezoneFor(iso as CountryCode);
      if (tz) setTimezone(tz);
    }
  }

  async function saveProfile() {
    setSaving('profile');
    setMessage(null);
    try {
      await apiFetch('/api/v1/me', {
        method: 'PATCH',
        body: {
          firstName: firstName.trim() || null,
          lastName: lastName.trim() || null,
          displayName: displayName || undefined,
          phone: phone || null,
          preferences: {
            country: country ?? undefined,
            timezone: timezone ?? undefined,
            digestTimeOfDay: digestTimeOfDay || undefined,
          },
        },
      });
      setMessage('Profile saved');
      // Topbar + sidebar `user` props come from the dashboard server
      // layout reading userProfiles.displayName - refresh so the new
      // name flows back through without a hard reload.
      router.refresh();
    } catch (err: unknown) {
      setMessage(err instanceof Error ? err.message : 'Failed to save');
    } finally {
      setSaving(null);
    }
  }

  async function saveUsername() {
    if (username.trim().toLowerCase() === originalUsername.toLowerCase()) return;
    setSaving('username');
    setUsernameMsg(null);
    try {
      const next = username.trim().toLowerCase() || null;
      await apiFetch('/api/v1/me', { method: 'PATCH', body: { username: next } });
      setOriginalUsername(next ?? '');
      setUsername(next ?? '');
      setUsernameMsg(
        next
          ? `Username updated. You can now sign in with @${next} or your email.`
          : 'Username cleared.',
      );
    } catch (err: unknown) {
      setUsernameMsg(err instanceof Error ? err.message : 'Failed to save username');
    } finally {
      setSaving(null);
    }
  }

  async function saveEmail() {
    if (email === originalEmail) return;
    setSaving('email');
    setEmailMsg(null);
    try {
      await apiFetch('/api/v1/me/email', { method: 'PATCH', body: { email } });
      setOriginalEmail(email);
      setEmailMsg('Email updated. Use the new address next time you sign in.');
      router.refresh();
    } catch (err: unknown) {
      setEmailMsg(err instanceof Error ? err.message : 'Failed to update email');
    } finally {
      setSaving(null);
    }
  }

  async function requestPasswordReset() {
    setSaving('password');
    setResetMsg(null);
    try {
      await apiFetch('/api/v1/me/password-reset', { method: 'POST' });
      setResetMsg('Password-reset email sent. Check your inbox.');
    } catch (err: unknown) {
      setResetMsg(err instanceof Error ? err.message : 'Failed to send reset email');
    } finally {
      setSaving(null);
    }
  }

  function adoptDetectedTz() {
    if (detectedTz) setTimezone(detectedTz);
  }

  const tzMismatch = detectedTz && timezone && detectedTz !== timezone;

  return (
    <div>
      <PageHeader title="Settings" description="Manage your profile and notification preferences" />

      <div className="mt-6 space-y-6">
        <Card>
          <CardHeader>
            <CardTitle>Profile</CardTitle>
            <CardDescription>Update your display name and contact info</CardDescription>
          </CardHeader>
          <CardContent className="space-y-4">
            {/* Avatar - click the photo to pick a file, which opens the
                cropper. Cropped result uploads via /api/v1/me/avatar. */}
            <div className="flex items-center gap-4">
              <Avatar className="h-16 w-16">
                {avatarUrl ? <AvatarImage src={avatarUrl} alt="Profile photo" /> : null}
                <AvatarFallback>
                  {(displayName || email || 'U').slice(0, 1).toUpperCase()}
                </AvatarFallback>
              </Avatar>
              <div className="space-y-1">
                <Label className="text-sm">Profile photo</Label>
                <div>
                  <Button
                    type="button"
                    variant="outline"
                    size="sm"
                    onClick={() => fileInputRef.current?.click()}
                  >
                    <Upload className="mr-1.5 h-3.5 w-3.5" />
                    {avatarFileId ? 'Replace photo' : 'Upload photo'}
                  </Button>
                  <input
                    ref={fileInputRef}
                    type="file"
                    accept="image/jpeg,image/png,image/webp"
                    onChange={handleAvatarPicked}
                    className="hidden"
                  />
                </div>
                <p className="text-xs text-muted-foreground">
                  PNG, JPG, or WebP up to 2 MB. You&apos;ll be able to crop after picking.
                </p>
              </div>
            </div>
            <div className="grid grid-cols-2 gap-3">
              <div className="space-y-2">
                <Label htmlFor="settings-first-name">First name</Label>
                <Input
                  id="settings-first-name"
                  value={firstName}
                  onChange={(e) => setFirstName(e.target.value)}
                  placeholder="Given name"
                  autoComplete="given-name"
                />
              </div>
              <div className="space-y-2">
                <Label htmlFor="settings-last-name">Last name</Label>
                <Input
                  id="settings-last-name"
                  value={lastName}
                  onChange={(e) => setLastName(e.target.value)}
                  placeholder="Family name"
                  autoComplete="family-name"
                />
              </div>
            </div>
            <div className="space-y-2">
              <Label htmlFor="settings-name">
                Display name <span className="text-muted-foreground">(optional)</span>
              </Label>
              <Input
                id="settings-name"
                value={displayName}
                onChange={(e) => setDisplayName(e.target.value)}
                placeholder="How your name appears in the UI"
              />
              <p className="text-xs text-muted-foreground">
                Defaults to first + last when blank. Override with a nickname if you prefer.
              </p>
            </div>
            <div className="space-y-2">
              <Label htmlFor="settings-phone">Phone</Label>
              <PhoneInput
                id="settings-phone"
                value={
                  phone
                    ? ({ e164: phone, country: (country as never) ?? 'US' } as PhoneInputValue)
                    : null
                }
                onChange={(next) => setPhone(next.e164 ?? '')}
                placeholder="555 0123"
              />
            </div>
            <div className="space-y-2">
              <Label>Country</Label>
              <CountryCombobox value={country} onChange={handleCountryChange} />
              <p className="text-xs text-muted-foreground">
                Sets the default timezone when you pick a country and haven&apos;t chosen one
                explicitly.
              </p>
            </div>
            <div className="space-y-2">
              <Label>Timezone</Label>
              <TimezoneCombobox
                value={timezone}
                onChange={setTimezone}
                countryHint={(country as CountryCode | null) ?? undefined}
              />
              {tzMismatch && (
                <WarningCallout icon={false}>
                  <span className="flex items-start gap-2 text-xs">
                    <Globe aria-hidden className="h-3.5 w-3.5 shrink-0 mt-0.5" />
                    <span className="flex-1">
                      Looks like you&apos;re in <strong>{detectedTz}</strong> right now (saved:{' '}
                      {timezone}).
                      <button
                        type="button"
                        onClick={adoptDetectedTz}
                        className="ml-1 underline underline-offset-2 hover:no-underline"
                      >
                        Update?
                      </button>
                    </span>
                  </span>
                </WarningCallout>
              )}
            </div>
            <div className="space-y-2">
              <Label htmlFor="settings-digest-tod">Default reminder time</Label>
              <Input
                id="settings-digest-tod"
                type="time"
                value={digestTimeOfDay}
                onChange={(e) => setDigestTimeOfDay(e.target.value)}
                className="w-32"
              />
              <p className="text-xs text-muted-foreground">
                When you create a reminder without picking a time, this is the time-of-day it
                defaults to. Per-reminder overrides still win.
              </p>
            </div>
            <div className="flex items-center gap-3">
              <Button onClick={saveProfile} disabled={saving === 'profile'}>
                <Save className="mr-1.5 h-4 w-4" />
                {saving === 'profile' ? 'Saving…' : 'Save profile'}
              </Button>
              {message && <span className="text-sm text-muted-foreground">{message}</span>}
            </div>
          </CardContent>
        </Card>

        <Card>
          <CardHeader>
            <CardTitle>Account</CardTitle>
            <CardDescription>Sign-in email and password</CardDescription>
          </CardHeader>
          <CardContent className="space-y-4">
            <div className="space-y-2">
              <Label htmlFor="settings-username">
                Username <span className="text-muted-foreground">(optional)</span>
              </Label>
              <Input
                id="settings-username"
                value={username}
                onChange={(e) => setUsername(e.target.value.toLowerCase())}
                placeholder="yourname"
                autoCapitalize="none"
                spellCheck={false}
                pattern="^[a-z0-9._-]{2,30}$"
              />
              <div className="flex items-center gap-3">
                <Button
                  size="sm"
                  variant="outline"
                  onClick={saveUsername}
                  disabled={
                    saving === 'username' ||
                    username.trim().toLowerCase() === originalUsername.toLowerCase()
                  }
                >
                  {saving === 'username' ? 'Saving…' : 'Save username'}
                </Button>
                {usernameMsg && (
                  <span className="text-xs text-muted-foreground">{usernameMsg}</span>
                )}
              </div>
              <p className="text-xs text-muted-foreground">
                Optional alias you can use to sign in instead of your email. 2–30 lowercase letters,
                digits, dot, underscore, or hyphen.
              </p>
            </div>
            <div className="space-y-2 pt-2 border-t">
              <Label htmlFor="settings-email">Email</Label>
              <Input
                id="settings-email"
                type="email"
                value={email}
                onChange={(e) => setEmail(e.target.value)}
              />
              <div className="flex items-center gap-3">
                <Button
                  size="sm"
                  variant="outline"
                  onClick={saveEmail}
                  disabled={saving === 'email' || email === originalEmail || !email}
                >
                  {saving === 'email' ? 'Updating…' : 'Update email'}
                </Button>
                {emailMsg && <span className="text-xs text-muted-foreground">{emailMsg}</span>}
              </div>
              <p className="text-xs text-muted-foreground">
                Changing your email also changes the address you use to sign in.
              </p>
            </div>
            <div className="space-y-2 pt-2 border-t">
              <Label>Password</Label>
              <div className="flex items-center gap-3">
                <Button
                  size="sm"
                  variant="outline"
                  onClick={requestPasswordReset}
                  disabled={saving === 'password'}
                >
                  <KeyRound className="mr-1.5 h-3.5 w-3.5" />
                  {saving === 'password' ? 'Sending…' : 'Send reset email'}
                </Button>
                {resetMsg && <span className="text-xs text-muted-foreground">{resetMsg}</span>}
              </div>
              <p className="text-xs text-muted-foreground">
                We&apos;ll email you a link to set a new password.
              </p>
            </div>
          </CardContent>
        </Card>

        <ChangePasswordCard />

        <section id="notifications" className="space-y-4">
          <div>
            <h2 className="text-lg font-semibold">Notifications</h2>
            <p className="text-sm text-muted-foreground">
              Choose which notifications you receive and how they&apos;re delivered.
            </p>
          </div>
          <NotificationPreferencesForm />
          <ReminderDigestForm />
        </section>
      </div>

      <ImageCropperDialog
        open={cropperOpen}
        onOpenChange={setCropperOpen}
        file={pendingAvatarFile}
        aspect={1}
        outputWidth={256}
        title="Crop profile photo"
        onUpload={uploadAvatar}
      />
    </div>
  );
}

/** Direct change-password form (current + new). Lifted from the deprecated
 *  user-profile.tsx into the unified settings page so we keep both reset-via-
 *  email and change-in-place flows on a single screen. */
function ChangePasswordCard() {
  const [currentPassword, setCurrentPassword] = useState('');
  const [newPassword, setNewPassword] = useState('');
  const [confirmPassword, setConfirmPassword] = useState('');
  const [revokeOthers, setRevokeOthers] = useState(true);
  const [savingPassword, setSavingPassword] = useState(false);
  const [passwordMessage, setPasswordMessage] = useState<{
    kind: 'ok' | 'err';
    text: string;
  } | null>(null);

  async function changePassword(e: React.FormEvent) {
    e.preventDefault();
    setPasswordMessage(null);
    if (newPassword.length < 9) {
      setPasswordMessage({ kind: 'err', text: 'New password must be at least 9 characters' });
      return;
    }
    if (newPassword !== confirmPassword) {
      setPasswordMessage({ kind: 'err', text: 'New password and confirmation do not match' });
      return;
    }
    setSavingPassword(true);
    try {
      await apiFetch('/api/v1/me/password', {
        method: 'POST',
        body: { currentPassword, newPassword, revokeOtherSessions: revokeOthers },
      });
      setCurrentPassword('');
      setNewPassword('');
      setConfirmPassword('');
      setPasswordMessage({
        kind: 'ok',
        text: revokeOthers
          ? 'Password changed. Other sessions have been signed out.'
          : 'Password changed.',
      });
    } catch (err) {
      setPasswordMessage({
        kind: 'err',
        text: err instanceof Error ? err.message : 'Failed to change password',
      });
    } finally {
      setSavingPassword(false);
    }
  }

  return (
    <Card>
      <CardHeader>
        <CardTitle>Change password</CardTitle>
        <CardDescription>
          Minimum 9 characters. You&rsquo;ll be prompted to sign in again on your other devices if
          you check the box below.
        </CardDescription>
      </CardHeader>
      <CardContent>
        <form onSubmit={changePassword} className="space-y-4 max-w-md">
          <div>
            <Label htmlFor="currentPassword">Current password</Label>
            <Input
              id="currentPassword"
              type="password"
              autoComplete="current-password"
              required
              value={currentPassword}
              onChange={(e) => setCurrentPassword(e.target.value)}
              className="mt-1"
            />
          </div>
          <div>
            <Label htmlFor="newPassword">New password</Label>
            <Input
              id="newPassword"
              type="password"
              autoComplete="new-password"
              required
              minLength={9}
              value={newPassword}
              onChange={(e) => setNewPassword(e.target.value)}
              className="mt-1"
            />
          </div>
          <div>
            <Label htmlFor="confirmPassword">Confirm new password</Label>
            <Input
              id="confirmPassword"
              type="password"
              autoComplete="new-password"
              required
              minLength={9}
              value={confirmPassword}
              onChange={(e) => setConfirmPassword(e.target.value)}
              className="mt-1"
            />
          </div>
          <div className="flex items-center gap-2">
            <input
              id="revokeOthers"
              type="checkbox"
              checked={revokeOthers}
              onChange={(e) => setRevokeOthers(e.target.checked)}
              className="h-4 w-4"
            />
            <Label htmlFor="revokeOthers" className="text-sm font-normal cursor-pointer">
              Sign out of other devices
            </Label>
          </div>
          <div className="flex items-center gap-3">
            <Button type="submit" disabled={savingPassword} size="sm">
              Change password
            </Button>
            {passwordMessage ? (
              <span
                className={
                  passwordMessage.kind === 'ok' ? 'text-sm text-green-600' : 'text-sm text-red-600'
                }
              >
                {passwordMessage.text}
              </span>
            ) : null}
          </div>
        </form>
      </CardContent>
    </Card>
  );
}
