import { useCallback, useEffect, useState } from "react"

import { useArcadiaClient } from "@crema/arcadia-core-client"
import { useToast } from "@crema/notification-ui"

import { TenantSection, Field } from "~/components/tenant-detail/section"
import { Button } from "~/components/ui/button"
import { Input } from "~/components/ui/input"
import { Switch } from "~/components/ui/switch"
import { NativeSelect, NativeSelectOption } from "~/components/ui/native-select"
import {
  EMAIL_PROVIDERS,
  SMS_PROVIDERS,
  deleteEmailConfig,
  deleteSmsConfig,
  getEmailConfig,
  getSmsConfig,
  testEmailConfig,
  testSmsConfig,
  upsertEmailConfig,
  upsertSmsConfig,
  type EmailConfigInput,
  type SmsConfigInput,
} from "~/lib/arcadia/tenants"
import { errorMessage } from "~/lib/errors"
import type { TenantTabProps } from "~/routes/tenants.$id"

/**
 * Email & SMS delivery. These are two independent configs — each loads, saves,
 * tests, and deletes on its own — so the tab renders the same <DeliveryConfig>
 * frame twice, parameterised by `kind`.
 */
export function DeliveryTab({ tenant, reload }: TenantTabProps) {
  return (
    <div className="flex flex-col gap-6">
      <DeliveryConfig kind="email" tenant={tenant} reload={reload} />
      <DeliveryConfig kind="sms" tenant={tenant} reload={reload} />
    </div>
  )
}

type Kind = "email" | "sms"

/** A credential input the operator can fill. Values are collected into the
 *  write-only `credentials` object and only sent when actually typed. */
type CredField = { key: string; label: string; type?: string }

function credentialFields(kind: Kind, provider: string): CredField[] {
  if (kind === "email") {
    if (provider === "smtp") {
      return [
        { key: "host", label: "SMTP host" },
        { key: "port", label: "Port" },
        { key: "username", label: "Username" },
        { key: "password", label: "Password", type: "password" },
      ]
    }
    return [{ key: "api_key", label: "API key", type: "password" }]
  }
  // sms
  if (provider === "twilio") {
    return [
      { key: "account_sid", label: "Account SID" },
      { key: "auth_token", label: "Auth token", type: "password" },
    ]
  }
  if (provider === "vonage") {
    return [
      { key: "api_key", label: "API key" },
      { key: "api_secret", label: "API secret", type: "password" },
    ]
  }
  return [{ key: "api_key", label: "API key", type: "password" }]
}

function DeliveryConfig({ tenant, kind }: TenantTabProps & { kind: Kind }) {
  const arcadia = useArcadiaClient()
  const toast = useToast()

  const providers = kind === "email" ? EMAIL_PROVIDERS : SMS_PROVIDERS
  const prefix = `tenant-detail-${kind}`
  const label = kind === "email" ? "Email" : "SMS"

  const [loading, setLoading] = useState(true)
  const [error, setError] = useState<unknown>(null)
  const [saving, setSaving] = useState(false)
  const [testing, setTesting] = useState(false)
  const [removing, setRemoving] = useState(false)
  // Whether a config exists server-side (loaded non-null, or just saved). Gates
  // "Send test" and "Remove", which are meaningless with nothing configured.
  const [exists, setExists] = useState(false)

  const [provider, setProvider] = useState<string>(providers[0])
  const [enabled, setEnabled] = useState(false)
  // email-only
  const [fromEmail, setFromEmail] = useState("")
  const [fromName, setFromName] = useState("")
  const [replyTo, setReplyTo] = useState("")
  // sms-only
  const [fromNumber, setFromNumber] = useState("")
  // write-only credentials, keyed by field
  const [creds, setCreds] = useState<Record<string, string>>({})

  const load = useCallback(async () => {
    setError(null)
    setLoading(true)
    try {
      if (kind === "email") {
        const cfg = await getEmailConfig(arcadia, tenant.id)
        if (cfg) {
          setExists(true)
          setProvider(cfg.provider)
          setEnabled(cfg.enabled)
          setFromEmail(cfg.from_email ?? "")
          setFromName(cfg.from_name ?? "")
          setReplyTo(cfg.reply_to ?? "")
        } else {
          setExists(false)
        }
      } else {
        const cfg = await getSmsConfig(arcadia, tenant.id)
        if (cfg) {
          setExists(true)
          setProvider(cfg.provider)
          setEnabled(cfg.enabled)
          setFromNumber(cfg.from_number ?? "")
        } else {
          setExists(false)
        }
      }
      // Credentials are never returned — always start the write-only fields empty.
      setCreds({})
    } catch (err) {
      // getEmailConfig/getSmsConfig already map 404 → null, so anything thrown
      // here is a genuine load failure.
      setError(err)
    } finally {
      setLoading(false)
    }
  }, [arcadia, tenant.id, kind])

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

  const save = async () => {
    setSaving(true)
    setError(null)
    try {
      // Only send credentials the operator actually typed, scoped to the
      // current provider — otherwise we'd wipe stored creds with blanks, or
      // leak a previous provider's fields.
      const typedCreds: Record<string, string> = {}
      for (const f of credentialFields(kind, provider)) {
        const v = (creds[f.key] ?? "").trim()
        if (v) typedCreds[f.key] = v
      }
      const hasCreds = Object.keys(typedCreds).length > 0

      if (kind === "email") {
        const input: EmailConfigInput = {
          provider,
          from_email: fromEmail,
          from_name: fromName,
          reply_to: replyTo,
          enabled,
          ...(hasCreds ? { credentials: typedCreds } : {}),
        }
        await upsertEmailConfig(arcadia, tenant.id, input)
        toast.success("Email settings saved")
      } else {
        const input: SmsConfigInput = {
          provider,
          from_number: fromNumber,
          enabled,
          ...(hasCreds ? { credentials: typedCreds } : {}),
        }
        await upsertSmsConfig(arcadia, tenant.id, input)
        toast.success("SMS settings saved")
      }
      await load()
    } catch (err) {
      setError(err)
    } finally {
      setSaving(false)
    }
  }

  const runTest = async () => {
    setTesting(true)
    try {
      const res =
        kind === "email"
          ? await testEmailConfig(arcadia, tenant.id)
          : await testSmsConfig(arcadia, tenant.id)
      if (res.ok) toast.success(res.message || `${label} test succeeded`)
      else toast.error(res.message || `${label} test failed`)
    } catch (err) {
      toast.error(errorMessage(err, `test ${label.toLowerCase()} delivery`))
    } finally {
      setTesting(false)
    }
  }

  const remove = async () => {
    setRemoving(true)
    try {
      if (kind === "email") await deleteEmailConfig(arcadia, tenant.id)
      else await deleteSmsConfig(arcadia, tenant.id)
      toast.success(`${label} configuration removed`)
      await load()
    } catch (err) {
      toast.error(errorMessage(err, `remove ${label.toLowerCase()} configuration`))
    } finally {
      setRemoving(false)
    }
  }

  const fields = credentialFields(kind, provider)

  return (
    <TenantSection
      title={`${label} delivery`}
      description={
        kind === "email"
          ? "Outbound email for this tenant. Credentials are write-only and never shown again."
          : "Outbound SMS for this tenant. Credentials are write-only and never shown again."
      }
      onSubmit={save}
      saving={saving || loading}
      error={error}
      errorContext={`save ${label.toLowerCase()} settings`}
      saveLabel={kind === "email" ? "Save email settings" : "Save SMS settings"}
      dataAction={`${prefix}-save`}
      footerExtra={
        exists ? (
          <div className="flex items-center gap-2">
            <Button
              type="button"
              variant="outline"
              onClick={runTest}
              disabled={testing || saving}
              data-action={`${prefix}-test`}
            >
              {testing ? "Testing…" : "Send test"}
            </Button>
            <Button
              type="button"
              variant="ghost"
              className="text-destructive"
              onClick={remove}
              disabled={removing || saving}
              data-action={`${prefix}-remove`}
            >
              {removing ? "Removing…" : "Remove configuration"}
            </Button>
          </div>
        ) : null
      }
    >
      <Field label="Provider" htmlFor={`${prefix}-provider`}>
        <NativeSelect
          id={`${prefix}-provider`}
          className="w-full"
          value={provider}
          onChange={(e) => setProvider(e.target.value)}
          data-action={`${prefix}-provider`}
        >
          {providers.map((p) => (
            <NativeSelectOption key={p} value={p}>
              {p}
            </NativeSelectOption>
          ))}
        </NativeSelect>
      </Field>

      {kind === "email" ? (
        <>
          <Field label="From email" htmlFor={`${prefix}-from-email`}>
            <Input
              id={`${prefix}-from-email`}
              type="email"
              value={fromEmail}
              onChange={(e) => setFromEmail(e.target.value)}
              placeholder="no-reply@example.com"
              data-action={`${prefix}-from-email`}
            />
          </Field>
          <Field label="From name" htmlFor={`${prefix}-from-name`}>
            <Input
              id={`${prefix}-from-name`}
              value={fromName}
              onChange={(e) => setFromName(e.target.value)}
              placeholder="Example App"
              data-action={`${prefix}-from-name`}
            />
          </Field>
          <Field label="Reply-to" htmlFor={`${prefix}-reply-to`}>
            <Input
              id={`${prefix}-reply-to`}
              type="email"
              value={replyTo}
              onChange={(e) => setReplyTo(e.target.value)}
              placeholder="support@example.com"
              data-action={`${prefix}-reply-to`}
            />
          </Field>
        </>
      ) : (
        <Field
          label="From number"
          htmlFor={`${prefix}-from-number`}
          hint="The sender number or short code, in E.164 (e.g. +15551234567)."
        >
          <Input
            id={`${prefix}-from-number`}
            value={fromNumber}
            onChange={(e) => setFromNumber(e.target.value)}
            placeholder="+15551234567"
            data-action={`${prefix}-from-number`}
          />
        </Field>
      )}

      <div className="flex items-center justify-between gap-3">
        <div>
          <div className="text-sm font-medium">Enabled</div>
          <p className="text-xs text-muted-foreground">
            When off, {label.toLowerCase()} is configured but not sent.
          </p>
        </div>
        <Switch
          checked={enabled}
          onCheckedChange={(v) => setEnabled(v)}
          data-action={`${prefix}-enabled`}
        />
      </div>

      <div className="flex flex-col gap-4 rounded-lg border border-input bg-muted/30 p-4">
        <div>
          <div className="text-sm font-medium">Credentials</div>
          <p className="text-xs text-muted-foreground">
            {exists
              ? "Write-only and never shown again. Leave blank to keep the stored credentials; fill in to replace them."
              : "Write-only and never shown again."}
          </p>
        </div>
        {fields.map((f) => (
          <Field key={f.key} label={f.label} htmlFor={`${prefix}-cred-${f.key}`}>
            <Input
              id={`${prefix}-cred-${f.key}`}
              type={f.type ?? "text"}
              autoComplete="off"
              value={creds[f.key] ?? ""}
              onChange={(e) => setCreds((c) => ({ ...c, [f.key]: e.target.value }))}
              placeholder={exists ? "•••••• (unchanged)" : ""}
              data-action={`${prefix}-cred-${f.key}`}
            />
          </Field>
        ))}
      </div>
    </TenantSection>
  )
}
