import { useMemo, useState } from 'react'
import { useNavigate } from 'react-router-dom'
import { Loader2, Plus, X } from 'lucide-react'
import { useAuthStore } from '@/store/authStore'
import { onboardingApi } from '@/api/onboarding'
import { accountsApi, type BulkInviteRow } from '@/api/accounts'
import { toast } from '@/lib/toast'
import { cn } from '@/lib/utils'

const MAX_ROWS = 10
const DEFAULT_ROW_COUNT = 3

type RowRole = 'engineer' | 'viewer'

interface InviteRow {
  email: string
  role: RowRole
  /**
   * Server-returned per-row error (from `failed[]`). Kept on the row so
   * users can fix and retry without losing the rest of their input.
   */
  error?: string
}

const ROLE_OPTIONS: { value: RowRole; label: string }[] = [
  { value: 'engineer', label: 'Tech' },
  { value: 'viewer', label: 'Viewer' },
]

const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/

function makeEmptyRow(): InviteRow {
  return { email: '', role: 'engineer' }
}

/**
 * `/welcome/step-3` — final step of the welcome wizard. Captures up to
 * `MAX_ROWS` teammate invites. On submit:
 *
 *   1. POST `/accounts/me/invites/bulk` with populated rows.
 *   2. PATCH `/users/me/onboarding-step` `{step: 3, action: "complete"}`.
 *   3. Navigate to `/home` and fire a "You're all set" toast.
 *
 * Partial-failure UX: rows in `failed[]` keep their input and show an
 * inline error. The wizard does NOT auto-advance when there are failures —
 * the user can edit and retry, OR click "Continue anyway" to mark step 3
 * complete and head to the dashboard.
 *
 * Empty rows are filtered before submit, so empty-form + "Send" is a no-op
 * that just marks the step complete. (Skip does the same with `action: skip`.)
 */
export function WelcomeStep3() {
  const navigate = useNavigate()
  const fetchUser = useAuthStore((s) => s.fetchUser)

  const [rows, setRows] = useState<InviteRow[]>(() =>
    Array.from({ length: DEFAULT_ROW_COUNT }, makeEmptyRow),
  )
  const [submitting, setSubmitting] = useState<
    'send' | 'skip' | 'dismiss' | 'continue-anyway' | null
  >(null)
  const [error, setError] = useState<string | null>(null)
  const [hasUnresolvedFailures, setHasUnresolvedFailures] = useState(false)

  const isBusy = submitting !== null

  const updateRow = (idx: number, patch: Partial<InviteRow>) => {
    setRows((prev) =>
      prev.map((row, i) => (i === idx ? { ...row, ...patch } : row)),
    )
  }

  const removeRow = (idx: number) => {
    setRows((prev) => {
      if (prev.length <= 1) return [makeEmptyRow()]
      return prev.filter((_, i) => i !== idx)
    })
  }

  const addRow = () => {
    setRows((prev) =>
      prev.length >= MAX_ROWS ? prev : [...prev, makeEmptyRow()],
    )
  }

  /**
   * Validate populated rows. Empty-email rows are dropped silently.
   * Returns either the list of valid rows OR a per-index error map.
   */
  const validatePopulated = useMemo(
    () => () => {
      const errs: Record<number, string> = {}
      const populated: { idx: number; row: BulkInviteRow }[] = []
      rows.forEach((row, idx) => {
        const email = row.email.trim()
        if (!email) return
        if (!EMAIL_RE.test(email)) {
          errs[idx] = 'Invalid email'
          return
        }
        populated.push({ idx, row: { email, role: row.role } })
      })
      return { errs, populated }
    },
    [rows],
  )

  const completeWizardAndExit = async () => {
    await onboardingApi.updateStep({ step: 3, action: 'complete' })
    await fetchUser()
    toast.success("You're all set!")
    navigate('/home')
  }

  const handleSendInvites = async () => {
    if (isBusy) return
    setError(null)

    const { errs, populated } = validatePopulated()
    if (Object.keys(errs).length > 0) {
      // Surface client-side validation errors inline.
      setRows((prev) =>
        prev.map((row, idx) =>
          errs[idx] ? { ...row, error: errs[idx] } : { ...row, error: undefined },
        ),
      )
      return
    }

    setSubmitting('send')
    try {
      let failedSet = new Map<string, string>()
      if (populated.length > 0) {
        const result = await accountsApi.bulkInvite(populated.map((p) => p.row))
        failedSet = new Map(result.failed.map((f) => [f.email, f.error]))
      }

      if (failedSet.size > 0) {
        // Stamp errors on the matching rows; do NOT auto-advance.
        setRows((prev) =>
          prev.map((row) => {
            const email = row.email.trim()
            const err = email ? failedSet.get(email) : undefined
            return { ...row, error: err }
          }),
        )
        setHasUnresolvedFailures(true)
        setSubmitting(null)
        return
      }

      // All-clear (or zero invites sent): mark step complete and exit.
      await completeWizardAndExit()
    } catch {
      setError('Could not send invites. Please try again.')
      setSubmitting(null)
    }
  }

  const handleContinueAnyway = async () => {
    if (isBusy) return
    setError(null)
    setSubmitting('continue-anyway')
    try {
      await completeWizardAndExit()
    } catch {
      setError('Could not save. Please try again.')
      setSubmitting(null)
    }
  }

  const handleSkipStep = async () => {
    if (isBusy) return
    setError(null)
    setSubmitting('skip')
    try {
      await onboardingApi.updateStep({ step: 3, action: 'skip' })
      await fetchUser()
      toast.success("You're all set!")
      navigate('/home')
    } catch {
      setError('Could not save. Please try again.')
      setSubmitting(null)
    }
  }

  const handleDismissRest = async () => {
    if (isBusy) return
    setError(null)
    setSubmitting('dismiss')
    try {
      await onboardingApi.dismissRest()
      await fetchUser()
      navigate('/home')
    } catch {
      setError('Could not save. Please try again.')
      setSubmitting(null)
    }
  }

  const inputClass = cn(
    'block w-full rounded-xl border border-border bg-card px-3 py-2',
    'text-foreground placeholder:text-muted-foreground',
    'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20',
  )

  return (
    <div className="mx-auto w-full max-w-2xl px-4 py-10">
      <header className="mb-8">
        <p className="text-xs uppercase tracking-wider text-muted-foreground">
          Step 3 of 3
        </p>
        <h1 className="mt-2 text-2xl font-heading font-bold text-text-heading">
          Invite your team
        </h1>
        <p className="mt-2 text-sm text-muted-foreground">
          Add up to {MAX_ROWS} teammates. They'll get an email with a link to
          join. Leave blank to do this later.
        </p>
      </header>

      <div
        className="rounded-2xl border border-border bg-card p-6 space-y-4"
        data-testid="welcome-step-3-form"
      >
        <div className="space-y-3">
          {rows.map((row, idx) => (
            <div key={idx} className="space-y-1">
              <div className="flex items-start gap-2">
                <input
                  type="email"
                  value={row.email}
                  onChange={(e) => updateRow(idx, { email: e.target.value, error: undefined })}
                  placeholder="teammate@example.com"
                  className={cn(inputClass, 'flex-1')}
                  data-testid={`welcome-step-3-email-${idx}`}
                  disabled={isBusy}
                />
                <select
                  value={row.role}
                  onChange={(e) =>
                    updateRow(idx, { role: e.target.value as RowRole })
                  }
                  className={cn(inputClass, 'w-32 flex-shrink-0')}
                  data-testid={`welcome-step-3-role-${idx}`}
                  disabled={isBusy}
                >
                  {ROLE_OPTIONS.map((opt) => (
                    <option key={opt.value} value={opt.value}>
                      {opt.label}
                    </option>
                  ))}
                </select>
                <button
                  type="button"
                  onClick={() => removeRow(idx)}
                  disabled={isBusy || rows.length <= 1}
                  data-testid={`welcome-step-3-remove-${idx}`}
                  aria-label="Remove row"
                  className={cn(
                    'inline-flex h-10 w-10 items-center justify-center rounded-xl',
                    'text-muted-foreground hover:bg-foreground/5 hover:text-foreground',
                    'focus:outline-hidden focus:ring-2 focus:ring-primary/20',
                    'disabled:opacity-30 disabled:hover:bg-transparent',
                  )}
                >
                  <X className="h-4 w-4" />
                </button>
              </div>
              {row.error && (
                <p
                  className="pl-1 text-xs text-red-400"
                  data-testid={`welcome-step-3-row-error-${idx}`}
                >
                  {row.error}
                </p>
              )}
            </div>
          ))}
        </div>

        <button
          type="button"
          onClick={addRow}
          disabled={isBusy || rows.length >= MAX_ROWS}
          data-testid="welcome-step-3-add-row"
          className={cn(
            'inline-flex items-center gap-1.5 rounded-xl px-2 py-1 text-xs font-medium',
            'text-muted-foreground hover:bg-foreground/5 hover:text-foreground',
            'focus:outline-hidden focus:ring-2 focus:ring-primary/20',
            'disabled:opacity-50',
          )}
        >
          <Plus className="h-3.5 w-3.5" />
          Add another
        </button>

        {error && (
          <p className="text-xs text-red-400" data-testid="welcome-step-3-error">
            {error}
          </p>
        )}

        <div className="flex flex-wrap items-center gap-3 pt-2">
          <button
            type="button"
            onClick={handleSendInvites}
            disabled={isBusy}
            data-testid="welcome-step-3-send"
            className={cn(
              'inline-flex items-center justify-center gap-2 rounded-xl px-4 py-2.5 text-sm font-semibold btn-press',
              'bg-primary text-primary-foreground hover:bg-primary/90',
              'focus:outline-hidden focus:ring-2 focus:ring-primary/30',
              'disabled:opacity-60',
            )}
          >
            {submitting === 'send' && (
              <Loader2 className="h-4 w-4 animate-spin" />
            )}
            Send invites and continue
          </button>
          {hasUnresolvedFailures && (
            <button
              type="button"
              onClick={handleContinueAnyway}
              disabled={isBusy}
              data-testid="welcome-step-3-continue-anyway"
              className={cn(
                'inline-flex items-center justify-center gap-2 rounded-xl px-4 py-2.5 text-sm font-medium btn-press',
                'bg-card border border-border text-foreground hover:bg-foreground/5',
                'focus:outline-hidden focus:ring-2 focus:ring-primary/20',
                'disabled:opacity-60',
              )}
            >
              {submitting === 'continue-anyway' && (
                <Loader2 className="h-4 w-4 animate-spin" />
              )}
              Continue anyway
            </button>
          )}
          <button
            type="button"
            onClick={handleSkipStep}
            disabled={isBusy}
            data-testid="welcome-step-3-skip"
            className={cn(
              'text-sm text-muted-foreground hover:underline',
              'disabled:opacity-60',
            )}
          >
            {submitting === 'skip' ? 'Saving…' : 'Skip'}
          </button>
        </div>
      </div>

      <div className="mt-6 text-center">
        <button
          type="button"
          onClick={handleDismissRest}
          disabled={isBusy}
          data-testid="welcome-step-3-dismiss-rest"
          className={cn(
            'text-xs text-muted-foreground hover:underline',
            'disabled:opacity-60',
          )}
        >
          {submitting === 'dismiss' ? 'Saving…' : 'Skip the rest'}
        </button>
      </div>
    </div>
  )
}

export default WelcomeStep3
