"use client";

import { AppShell } from "../../../components/app-shell";
import { useCallback, useEffect, useState } from "react";
import { usePlaidLink } from "react-plaid-link";

type Account = {
  viewRef: string;
  institutionName: string;
  accountType: string;
  mask?: string | null;
  syncStatus?: string | null;
  lastSyncError?: string | null;
  plaidWebhookCode?: string | null;
  tellerConnected?: boolean;
};

type TellerEnrollment = {
  accessToken: string;
  user?: { id?: string };
  enrollment?: {
    id?: string;
    institution?: { name?: string };
  };
};

declare global {
  interface Window {
    TellerConnect?: {
      setup(config: {
        applicationId: string;
        environment?: string;
        products: string[];
        enrollmentId?: string;
        onSuccess(enrollment: TellerEnrollment): void;
        onExit?(): void;
        onFailure?(error: unknown): void;
      }): { open(): void };
    };
  }
}

export default function ConnectPage() {
  const [status, setStatus] = useState("");
  const [linkToken, setLinkToken] = useState<string | null>(null);
  const [updateAccountRef, setUpdateAccountRef] = useState<string | null>(null);
  const [linkMode, setLinkMode] = useState<"connect" | "update">("connect");
  const [pendingOpen, setPendingOpen] = useState(false);
  const [manualMode, setManualMode] = useState(false);
  const [manualBank, setManualBank] = useState("");
  const [manualRouting, setManualRouting] = useState("");
  const [manualAccount, setManualAccount] = useState("");
  const [manualType, setManualType] = useState("checking");
  const [accounts, setAccounts] = useState<Account[]>([]);
  const [tellerReady, setTellerReady] = useState(false);

  const createLinkToken = useCallback(async () => {
    setStatus("Requesting Plaid link token...");
    try {
      const res = await fetch("/api/plaid/link-token", { method: "POST" });
      const payload = await res.json();
      if (!res.ok || payload.error) {
        setStatus(payload.error?.message ?? "Unable to create link token.");
        return;
      }
      const token = payload.data?.linkToken ?? payload.data?.link_token;
      if (token) {
        setLinkToken(token);
        setStatus("Link token ready.");
      } else {
        setStatus("Link token created.");
      }
    } catch {
      setStatus("Unable to create link token.");
    }
  }, []);

  const loadAccounts = useCallback(async () => {
    const res = await fetch("/api/accounts");
    if (!res.ok) {
      return;
    }
    const payload = await res.json();
    setAccounts(payload.data?.accounts ?? payload.data ?? []);
  }, []);

  const loadTellerScript = useCallback(() => {
    if (typeof window === "undefined") return Promise.resolve(false);
    if (window.TellerConnect) {
      setTellerReady(true);
      return Promise.resolve(true);
    }
    return new Promise<boolean>((resolve) => {
      const existing = document.querySelector<HTMLScriptElement>("script[data-teller-connect]");
      if (existing) {
        existing.addEventListener("load", () => {
          setTellerReady(Boolean(window.TellerConnect));
          resolve(Boolean(window.TellerConnect));
        });
        existing.addEventListener("error", () => resolve(false));
        return;
      }
      const script = document.createElement("script");
      script.src = "https://cdn.teller.io/connect/connect.js";
      script.dataset.tellerConnect = "true";
      script.onload = () => {
        setTellerReady(Boolean(window.TellerConnect));
        resolve(Boolean(window.TellerConnect));
      };
      script.onerror = () => resolve(false);
      document.body.appendChild(script);
    });
  }, []);

  useEffect(() => {
    createLinkToken();
    loadAccounts();
    loadTellerScript();
  }, [createLinkToken, loadAccounts, loadTellerScript]);

  const onSuccess = useCallback(
    async (publicToken: string | null) => {
      if (linkMode === "update" && updateAccountRef) {
        setStatus("Finishing bank reconnection...");
        try {
          const res = await fetch("/api/plaid/repair-complete", {
            method: "POST",
            headers: { "Content-Type": "application/json" },
            body: JSON.stringify({ accountId: updateAccountRef })
          });
          const payload = await res.json();
          if (!res.ok || payload.error) {
            setStatus(payload.error?.message ?? "Unable to finish bank reconnection.");
            return;
          }
          setStatus("Bank connection repaired.");
          setUpdateAccountRef(null);
          setLinkMode("connect");
          setLinkToken(null);
          await loadAccounts();
          await createLinkToken();
        } catch {
          setStatus("Unable to finish bank reconnection.");
        }
        return;
      }

      if (!publicToken) {
        setStatus("Plaid did not return a public token.");
        return;
      }

      setStatus("Exchanging public token...");
      try {
        const res = await fetch("/api/plaid/exchange", {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({ publicToken })
        });
        const payload = await res.json();
        if (!res.ok || payload.error) {
          setStatus(payload.error?.message ?? "Unable to exchange token.");
          return;
        }
        setStatus("Bank account connected.");
        setLinkToken(null);
        await loadAccounts();
        await createLinkToken();
      } catch {
        setStatus("Unable to exchange token.");
      }
    },
    [createLinkToken, linkMode, loadAccounts, updateAccountRef]
  );

  const { open, ready } = usePlaidLink({
    token: linkToken,
    onSuccess,
    onExit: () => {
      setStatus("Plaid Link closed.");
    }
  });

  useEffect(() => {
    if (pendingOpen && ready) {
      setPendingOpen(false);
      open();
    }
  }, [open, pendingOpen, ready]);

  const onManualSubmit = (event: React.FormEvent) => {
    event.preventDefault();
    const payload = {
      institutionName: manualBank,
      accountType: manualType,
      mask: manualAccount.slice(-4)
    };
    fetch("/api/accounts/manual", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(payload)
    })
      .then((res) => res.json())
      .then((data) => {
        if (data?.error) {
          setStatus(data.error?.message ?? "Unable to save manual account.");
          return;
        }
        setStatus(`Manual account saved for ${manualBank}.`);
        setManualBank("");
        setManualRouting("");
        setManualAccount("");
        setManualType("checking");
        loadAccounts();
      })
      .catch(() => {
        setStatus("Unable to save manual account.");
      });
  };

  const startUpdateMode = async (accountRef: string) => {
    setStatus("Requesting Plaid update-mode link token...");
    try {
      const res = await fetch("/api/plaid/update-link-token", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ accountId: accountRef })
      });
      const payload = await res.json();
      if (!res.ok || payload.error) {
        setStatus(payload.error?.message ?? "Unable to create update-mode link token.");
        return;
      }
      const token = payload.data?.linkToken ?? payload.data?.link_token;
      if (!token) {
        setStatus("Unable to create update-mode link token.");
        return;
      }
      setUpdateAccountRef(accountRef);
      setLinkMode("update");
      setLinkToken(token);
      setStatus("Reconnect token ready. Opening Plaid...");
      setPendingOpen(true);
    } catch {
      setStatus("Unable to create update-mode link token.");
    }
  };

  const needsReconnect = (account: Account) =>
    ["needs_reauth", "attention_required"].includes(account.syncStatus ?? "");

  const startTellerConnect = async () => {
    setStatus("Preparing Teller Connect...");
    const loaded = await loadTellerScript();
    if (!loaded || !window.TellerConnect) {
      setStatus("Unable to load Teller Connect.");
      return;
    }

    const configRes = await fetch("/api/teller/config");
    const configPayload = await configRes.json();
    if (!configRes.ok || configPayload.error) {
      setStatus(configPayload.error?.message ?? "Teller is not configured.");
      return;
    }

    const connect = window.TellerConnect.setup({
      applicationId: configPayload.data.applicationId,
      environment: configPayload.data.environment,
      products: configPayload.data.products,
      onSuccess: async (enrollment) => {
        setStatus("Importing Teller accounts...");
        const res = await fetch("/api/teller/enrollment", {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify(enrollment)
        });
        const payload = await res.json();
        if (!res.ok || payload.error) {
          setStatus(payload.error?.message ?? "Unable to import Teller accounts.");
          return;
        }
        setStatus(`Teller connected ${payload.data?.accountCount ?? 0} account(s).`);
        await loadAccounts();
      },
      onExit: () => setStatus("Teller Connect closed."),
      onFailure: () => setStatus("Teller Connect failed.")
    });
    connect.open();
  };

  const syncTeller = async () => {
    setStatus("Syncing Teller transactions...");
    const res = await fetch("/api/teller/sync", { method: "POST" });
    const payload = await res.json();
    if (!res.ok || payload.error) {
      setStatus(payload.error?.message ?? "Unable to sync Teller transactions.");
      return;
    }
    setStatus(`Synced ${payload.data?.created ?? 0} Teller transaction(s).`);
    await loadAccounts();
  };

  return (
    <AppShell
      title="Connect a bank"
      subtitle="Link your first account to begin syncing transactions."
    >
      <div className="glass-panel p-6 rounded-2xl shadow-sm">
        <h2 className="text-lg font-bold text-foreground">Bank connections</h2>
        <p className="mt-2 text-sm text-muted-foreground">
          Securely connect your bank or card account to start syncing.
        </p>
        <div className="mt-6 flex flex-wrap gap-3">
          <button
            type="button"
            className="rounded-full bg-primary px-5 py-2 text-sm font-bold text-primary-foreground shadow-sm hover:bg-primary/90 transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
            onClick={() => (ready ? open() : createLinkToken())}
            disabled={!ready && !linkToken}
          >
            Connect with Plaid
          </button>
          <button
            type="button"
            className="rounded-full border border-border bg-background px-5 py-2 text-sm font-semibold text-foreground hover:bg-secondary transition-colors"
            onClick={startTellerConnect}
          >
            Connect with Teller
          </button>
          <button
            type="button"
            className="rounded-full border border-border bg-background px-5 py-2 text-sm font-semibold text-foreground hover:bg-secondary transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
            onClick={syncTeller}
            disabled={!accounts.some((account) => account.tellerConnected)}
          >
            Sync Teller
          </button>
          <button
            type="button"
            className="rounded-full border border-border bg-background px-5 py-2 text-sm font-semibold text-foreground hover:bg-secondary transition-colors"
            onClick={() => setManualMode((value) => !value)}
          >
            {manualMode ? "Hide manual entry" : "Enter bank details"}
          </button>
        </div>
        {status ? <p className="mt-4 text-xs font-medium text-primary">{status}</p> : null}
        {accounts.length ? (
          <div className="mt-6 space-y-3">
            <p className="text-xs uppercase tracking-[0.2em] text-muted-foreground font-semibold">
              Connected accounts
            </p>
            {accounts.map((account) => (
              <div
                key={account.viewRef}
                className="flex flex-col gap-3 rounded-xl border border-border bg-secondary/30 px-4 py-3 text-sm md:flex-row md:items-center md:justify-between"
              >
                <div>
                  <p className="font-bold text-foreground">{account.institutionName}</p>
                  <p className="text-xs text-muted-foreground">
                    {account.accountType} {account.mask ? `- ${account.mask}` : ""}
                  </p>
                  {account.lastSyncError ? (
                    <p className="mt-1 text-xs text-destructive">{account.lastSyncError}</p>
                  ) : null}
                  {account.plaidWebhookCode ? (
                    <p className="mt-1 text-xs text-muted-foreground">{account.plaidWebhookCode}</p>
                  ) : null}
                </div>
                <div className="flex items-center gap-2">
                  <span className="rounded-full bg-primary/10 px-3 py-1 text-xs font-medium text-primary">
                    {account.tellerConnected ? "teller" : account.syncStatus ?? "connected"}
                  </span>
                  {needsReconnect(account) ? (
                    <button
                      type="button"
                      className="rounded-full bg-primary px-4 py-2 text-xs font-bold text-primary-foreground shadow-sm hover:bg-primary/90 transition-colors"
                      onClick={() => startUpdateMode(account.viewRef)}
                    >
                      Reconnect
                    </button>
                  ) : null}
                </div>
              </div>
            ))}
          </div>
        ) : null}
        {manualMode ? (
          <form className="mt-6 grid gap-4 md:grid-cols-2" onSubmit={onManualSubmit}>
            <div className="space-y-2">
              <label className="text-xs uppercase tracking-[0.2em] text-muted-foreground font-semibold">
                Bank name
              </label>
              <input
                className="w-full rounded-xl border border-border bg-background/50 px-4 py-3 text-sm text-foreground focus:border-primary focus:ring-primary"
                value={manualBank}
                onChange={(event) => setManualBank(event.target.value)}
                required
              />
            </div>
            <div className="space-y-2">
              <label className="text-xs uppercase tracking-[0.2em] text-muted-foreground font-semibold">
                Account type
              </label>
              <select
                className="w-full rounded-xl border border-border bg-background/50 px-4 py-3 text-sm text-foreground focus:border-primary focus:ring-primary"
                value={manualType}
                onChange={(event) => setManualType(event.target.value)}
              >
                <option value="checking">Checking</option>
                <option value="savings">Savings</option>
                <option value="credit">Credit</option>
              </select>
            </div>
            <div className="space-y-2">
              <label className="text-xs uppercase tracking-[0.2em] text-muted-foreground font-semibold">
                Routing number
              </label>
              <input
                className="w-full rounded-xl border border-border bg-background/50 px-4 py-3 text-sm text-foreground focus:border-primary focus:ring-primary"
                value={manualRouting}
                onChange={(event) => setManualRouting(event.target.value)}
                required
              />
            </div>
            <div className="space-y-2">
              <label className="text-xs uppercase tracking-[0.2em] text-muted-foreground font-semibold">
                Account number
              </label>
              <input
                className="w-full rounded-xl border border-border bg-background/50 px-4 py-3 text-sm text-foreground focus:border-primary focus:ring-primary"
                value={manualAccount}
                onChange={(event) => setManualAccount(event.target.value)}
                required
              />
            </div>
            <div className="md:col-span-2">
              <button
                type="submit"
                className="rounded-full bg-primary px-5 py-2 text-sm font-bold text-primary-foreground hover:bg-primary/90 transition-colors"
              >
                Save bank details
              </button>
            </div>
          </form>
        ) : null}
        <p className="mt-4 text-xs text-muted-foreground">
          Free includes two connections. Pro supports ten active accounts, and Elite supports unlimited accounts.
        </p>
      </div>
    </AppShell>
  );
}
