import { Show, createSignal, createEffect, onCleanup } from "solid-js"
import { useNavigate, type RouteSectionProps } from "@solidjs/router"
import { ExtensionAccount, NostrConnectAccount, PasswordAccount, PrivateKeyAccount } from "applesauce-accounts/accounts"
import { PasswordSigner } from "applesauce-signers"
import QrScanner from "qr-scanner"
import QRCode from "qrcode"
import { accountManager, ensureSessionTenant, identity, PLATFORM_NAME } from "@/lib/state"
import { copyToClipboard } from "@/lib/clipboard"
import { validateBunkerUri } from "@/lib/validation"
import { decideKeyLogin, initialLoginTab, normalizeBunkerUrl, type Tab } from "@/lib/loginInput"
import useMinLoading from "@/lib/useMinLoading"
import LoginTabsScreen from "@/components/login/LoginTabsScreen"
import LoginSignerScreen from "@/components/login/LoginSignerScreen"
import LoginKeyScreen from "@/components/login/LoginKeyScreen"
import QrScannerOverlay from "@/components/login/QrScannerOverlay"

const NIP46_RELAYS = ['wss://bucket.coracle.social', 'wss://ephemeral.snowflare.cc']

type Screen = "tabs" | "nip46" | "key"
type SignerTab = "qr" | "paste"
type KeyTab = "plaintext" | "encrypted"

type LoginProps = {
  inModal?: boolean
  onClose?: () => void
  onAuthenticated?: () => void | Promise<void>
}

async function loadNostrConnectSigner() {
  return import("applesauce-signers").then((m) => m.NostrConnectSigner)
}

type LoginPageProps = LoginProps & Partial<RouteSectionProps<unknown>>

export default function Login(props: LoginPageProps = {}) {
  const navigate = useNavigate()
  const [tab, setTab] = createSignal<Tab>(initialLoginTab(Boolean(window.nostr)))
  const [rawLoading, setRawLoading] = createSignal(false)
  const loading = useMinLoading(() => rawLoading())
  const [error, setError] = createSignal("")
  const [screen, setScreen] = createSignal<Screen>("tabs")

  const [signerTab, setSignerTab] = createSignal<SignerTab>("qr")
  const [keyTab, setKeyTab] = createSignal<KeyTab>("plaintext")
  const [nostrConnectUri, setNostrConnectUri] = createSignal("")
  const [qrDataUrl, setQrDataUrl] = createSignal("")
  const [showScanner, setShowScanner] = createSignal(false)
  const [bunkerUrl, setBunkerUrl] = createSignal("")
  const [nsecValue, setNsecValue] = createSignal("")
  const [ncryptsecValue, setNcryptsecValue] = createSignal("")
  const [password, setPassword] = createSignal("")

  let scannerVideo: HTMLVideoElement | undefined
  let scanner: QrScanner | undefined
  let abortController: AbortController | undefined

  async function completeLogin(account: ExtensionAccount | NostrConnectAccount | PrivateKeyAccount | PasswordAccount) {
    accountManager.addAccount(account)
    accountManager.setActive(account)
    try {
      await ensureSessionTenant()
    } catch (e) {
      accountManager.removeAccount(account)
      throw e
    }
    await props.onAuthenticated?.()
  }

  // Shared effect wrapper for the four login handlers: clear the error, hold the
  // loading flag for the duration, and surface any thrown error with a per-handler
  // fallback message. The signer construction / completeLogin lives in `fn`.
  async function runLogin(fn: () => Promise<void>, fallbackMessage: string) {
    setError("")
    setRawLoading(true)
    try {
      await fn()
    } catch (e) {
      setError(e instanceof Error ? e.message : fallbackMessage)
    } finally {
      setRawLoading(false)
    }
  }

  function loginWithNip07() {
    return runLogin(async () => {
      await completeLogin(await ExtensionAccount.fromExtension())
    }, "Failed to login with extension")
  }

  function startNostrConnect() {
    return runLogin(async () => {
      const NostrConnectSigner = await loadNostrConnectSigner()
      const signer = new NostrConnectSigner({ relays: NIP46_RELAYS })
      const uri = signer.getNostrConnectURI({
        name: PLATFORM_NAME,
        url: window.location.origin,
      })
      setNostrConnectUri(uri)
      setQrDataUrl(await QRCode.toDataURL(uri, { width: 256, margin: 2 }))

      abortController = new AbortController()
      const timeout = window.setTimeout(() => abortController?.abort(), 60_000)

      try {
        await signer.waitForSigner(abortController.signal)
        const pubkey = await signer.getPublicKey()
        const account = new NostrConnectAccount(pubkey, signer)
        await completeLogin(account)
      } finally {
        window.clearTimeout(timeout)
      }
    }, "Failed to connect signer")
  }

  function loginWithBunker() {
    setError("")
    const validationError = validateBunkerUri(bunkerUrl())
    if (validationError) {
      setError(validationError)
      return
    }
    return runLogin(async () => {
      const uri = normalizeBunkerUrl(bunkerUrl())
      const NostrConnectSigner = await loadNostrConnectSigner()
      const signer = await NostrConnectSigner.fromBunkerURI(uri)
      const pubkey = await signer.getPublicKey()
      const account = new NostrConnectAccount(pubkey, signer)
      await completeLogin(account)
    }, "Invalid bunker URL")
  }

  function loginWithKeyMaterial() {
    return runLogin(async () => {
      const plan = decideKeyLogin({ ncryptsec: ncryptsecValue(), nsec: nsecValue(), password: password() })
      switch (plan.kind) {
        case "error":
          throw new Error(plan.message)
        case "ncryptsec": {
          const signer = await PasswordSigner.fromNcryptsec(plan.ncryptsec, plan.password)
          const pubkey = await signer.getPublicKey()
          await completeLogin(new PasswordAccount(pubkey, signer))
          break
        }
        case "nsec":
          await completeLogin(PrivateKeyAccount.fromKey(plan.key))
          break
      }
    }, "Invalid key")
  }

  function closeScanner() {
    setShowScanner(false)
    scanner?.destroy()
    scanner = undefined
  }

  async function openScanner() {
    setError("")
    setShowScanner(true)

    // Wait a tick for the modal video element to mount
    await new Promise(r => setTimeout(r, 0))
    if (!scannerVideo) return

    scanner = new QrScanner(
      scannerVideo,
      (result) => {
        setBunkerUrl(result.data)
        closeScanner()
      },
      { returnDetailedScanResult: true },
    )
    try {
      await scanner.start()
    } catch (e) {
      setError(e instanceof Error ? e.message : "Unable to access camera")
      closeScanner()
    }
  }

  function enterSignerScreen() {
    setError("")
    setScreen("nip46")
    setSignerTab("qr")
    if (!nostrConnectUri()) void startNostrConnect()
  }

  function copyUri() {
    void copyToClipboard(nostrConnectUri(), { successMessage: "Link copied" })
  }

  createEffect(() => {
    if (!props.inModal && identity()) {
      navigate("/relays")
    }
  })

  onCleanup(() => {
    abortController?.abort()
    scanner?.destroy()
  })

  return (
    <div class={props.inModal ? "w-full" : "min-h-screen bg-gradient-to-br from-gray-50 via-white to-gray-100 flex items-center justify-center p-6"}>
      <div class="w-full max-w-3xl">
        <div class="relative overflow-hidden rounded-2xl border border-gray-200 bg-white shadow-xl">
          <Show when={props.inModal && props.onClose}>
            <button
              type="button"
              class="absolute right-4 top-4 z-10 rounded-md p-1 text-gray-400 hover:bg-gray-100 hover:text-gray-700"
              onClick={() => props.onClose?.()}
              aria-label="Close"
            >
              <svg class="h-5 w-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6L6 18M6 6l12 12" /></svg>
            </button>
          </Show>
          <div class="absolute inset-0 bg-[radial-gradient(circle_at_top,_rgba(99,102,241,0.08),_transparent_45%)]" />
          <div class="relative grid gap-8 p-8 md:grid-cols-[1.2fr_1fr] md:p-10">
            <div class="space-y-6">
              <div class="inline-flex items-center gap-2 rounded-full border border-indigo-100 bg-indigo-50 px-3 py-1 text-xs font-semibold uppercase tracking-wide text-indigo-700">
                Secure Nostr Login
              </div>
              <div>
                <h1 class="text-2xl font-semibold text-gray-900 py-2">Welcome!</h1>
                <p class="mt-3 text-sm leading-6 text-gray-600">
                  Connect your Nostr account to manage relay hosting, billing, and access in one place.
                </p>
              </div>
              <div class="space-y-3 text-sm text-gray-600">
                <div class="flex items-start gap-3">
                  <span class="mt-1 h-2 w-2 rounded-full bg-indigo-500" />
                  <p>Own your identity with cryptographic sign-in.</p>
                </div>
                <div class="flex items-start gap-3">
                  <span class="mt-1 h-2 w-2 rounded-full bg-indigo-500" />
                  <p>No passwords or email required.</p>
                </div>
                <div class="flex items-start gap-3">
                  <span class="mt-1 h-2 w-2 rounded-full bg-indigo-500" />
                  <p>Get fast access to your relays.</p>
                </div>
              </div>
            </div>

            <div class="rounded-xl border border-gray-200 bg-white/80 p-6">
              <Show when={screen() === "tabs"}>
                <LoginTabsScreen
                  tab={tab}
                  setTab={setTab}
                  loading={loading}
                  hasExtension={Boolean(window.nostr)}
                  onContinueExtension={loginWithNip07}
                  onContinueSigner={enterSignerScreen}
                  onContinueKey={() => { setError(""); setScreen("key") }}
                />
              </Show>

              <Show when={screen() === "nip46"}>
                <LoginSignerScreen
                  signerTab={signerTab}
                  setSignerTab={setSignerTab}
                  qrDataUrl={qrDataUrl}
                  nostrConnectUri={nostrConnectUri}
                  bunkerUrl={bunkerUrl}
                  setBunkerUrl={setBunkerUrl}
                  loading={loading}
                  onBack={() => { setError(""); setScreen("tabs") }}
                  onCopyUri={copyUri}
                  onScan={openScanner}
                  onConnectBunker={loginWithBunker}
                />
              </Show>

              <Show when={screen() === "key"}>
                <LoginKeyScreen
                  keyTab={keyTab}
                  setKeyTab={setKeyTab}
                  nsecValue={nsecValue}
                  setNsecValue={setNsecValue}
                  ncryptsecValue={ncryptsecValue}
                  setNcryptsecValue={setNcryptsecValue}
                  password={password}
                  setPassword={setPassword}
                  loading={loading}
                  onBack={() => { setError(""); setScreen("tabs") }}
                  onSubmit={() => void loginWithKeyMaterial()}
                />
              </Show>

              <Show when={error()}>
                <p class="mt-4 text-xs text-red-600">{error()}</p>
              </Show>
            </div>
          </div>
        </div>
        <p class="mt-6 text-center text-xs text-gray-500">
          Having trouble? Make sure your signer is unlocked and connected.
        </p>
      </div>

      <QrScannerOverlay open={showScanner()} onClose={closeScanner} videoRef={(el) => { scannerVideo = el }} />
    </div>
  )
}
