"use client";

import type { AvailabilityResult, Brand, PurchaseResult, Region, RegistrarAccount } from "@domainops/shared-types";
import { App, Button, Card, Checkbox, Divider, Input, Select, Table, Tag, Typography } from "antd";
import { PlusOutlined } from "@ant-design/icons";
import type { ColumnsType } from "antd/es/table";
import Link from "next/link";
import { useRouter } from "next/navigation";
import { useCallback, useEffect, useState } from "react";
import { checkAvailability, listRegistrars, purchaseDomains } from "@/lib/api/acquire";
import { listBrands, listRegions } from "@/lib/api/domains";
import { ApiError } from "@/lib/api/client";
import { SORT_DIRECTIONS, textCompare } from "@/lib/utils/sort";
import { PAGE_SIZE_OPTIONS } from "@/lib/hooks/useServerPagination";
import { providerLabel } from "@/lib/utils/text";
import { BrandFormModal } from "../domains/brands/BrandFormModal";
import { PageGuide } from "@/components/features/PageGuide";
import { BUY_DOMAINS_GUIDES } from "@/lib/guides/acquire";

const TLD_OPTIONS = [".com", ".net", ".org", ".io", ".co", ".biz", ".info", ".online", ".app", ".xyz"];
const DEFAULT_TLDS = [".com", ".net", ".org"];

// Namecheap's API has no domain-suggestion/generator endpoint (confirmed
// against their docs) - the only way to offer creative alternates is to
// generate candidate names ourselves and run them through the same
// domains.check call as everything else. Kept to a small curated list so
// the combinatorial fqdn count stays predictable and well under Namecheap's
// documented 50-domains-per-call limit (see MAX_GENERATED_FQDNS below).
const SUGGESTION_PREFIXES = ["get", "my", "the", "go"];
const SUGGESTION_SUFFIXES = ["app", "hub", "pro", "online"];
const MAX_GENERATED_FQDNS = 45;

function generateNameVariants(base: string): string[] {
  const variants = new Set<string>([base]);
  for (const prefix of SUGGESTION_PREFIXES) {
    variants.add(`${prefix}${base}`);
    variants.add(`${prefix}-${base}`);
  }
  for (const suffix of SUGGESTION_SUFFIXES) {
    variants.add(`${base}${suffix}`);
    variants.add(`${base}-${suffix}`);
  }
  return Array.from(variants);
}

export default function AcquirePage() {
  const { message, modal } = App.useApp();
  const router = useRouter();
  const [registrars, setRegistrars] = useState<RegistrarAccount[]>([]);
  const [registrarId, setRegistrarId] = useState<number | undefined>();

  const [namesText, setNamesText] = useState("");
  const [tlds, setTlds] = useState<string[]>(DEFAULT_TLDS);
  const [results, setResults] = useState<AvailabilityResult[]>([]);
  const [selected, setSelected] = useState<Set<string>>(new Set());
  const [isChecking, setIsChecking] = useState(false);

  const [suggestBase, setSuggestBase] = useState("");
  const [isSuggesting, setIsSuggesting] = useState(false);

  const [brands, setBrands] = useState<Brand[]>([]);
  const [regions, setRegions] = useState<Region[]>([]);
  const [brandId, setBrandId] = useState<number | undefined>();
  const [isBrandModalOpen, setIsBrandModalOpen] = useState(false);
  const [regionId, setRegionId] = useState<number | undefined>();
  const [isPurchasing, setIsPurchasing] = useState(false);
  const [purchaseResults, setPurchaseResults] = useState<PurchaseResult[] | null>(null);

  const loadRegistrars = useCallback(async () => {
    try {
      const response = await listRegistrars();
      setRegistrars(response.accounts.filter((a) => a.is_active));
      setRegistrarId((current) => current ?? response.accounts.find((a) => a.is_active)?.id);
    } catch (err) {
      message.error(err instanceof ApiError ? err.message : "Couldn't load registrar accounts.");
    }
  }, [message]);

  useEffect(() => {
    // Registrar accounts live on the Laravel API - a client fetch on
    // mount, not a state-sync bug.
    // eslint-disable-next-line react-hooks/set-state-in-effect
    void loadRegistrars();
  }, [loadRegistrars]);

  useEffect(() => {
    void listBrands().then(setBrands);
    void listRegions().then(setRegions);
  }, []);

  async function handleCheck() {
    const lines = namesText
      .split("\n")
      .map((line) => line.trim())
      .filter(Boolean);

    if (lines.length === 0) return;

    // Bare names are expanded across every ticked TLD. A full domain (one
    // that already contains a dot) is always checked exactly as typed, plus
    // its own base name expanded across every other ticked TLD too - so
    // pasting "brand.com" surfaces "brand.net"/"brand.org"/etc the same way
    // a bare "brand" would, instead of only checking the exact extension.
    const fqdns = Array.from(
      new Set(
        lines.flatMap((line) => {
          if (!line.includes(".")) return tlds.map((tld) => `${line}${tld}`);

          const base = line.slice(0, line.indexOf("."));

          return [line, ...tlds.map((tld) => `${base}${tld}`)];
        }),
      ),
    );

    setIsChecking(true);
    setPurchaseResults(null);
    try {
      const availability = await checkAvailability(fqdns, registrarId);
      setResults(availability);
      setSelected(new Set(availability.filter((r) => r.available).map((r) => r.fqdn)));
    } catch (err) {
      message.error(err instanceof ApiError ? err.message : "Couldn't check availability.");
    } finally {
      setIsChecking(false);
    }
  }

  async function handleGenerateSuggestions() {
    const base = suggestBase.trim().replace(/\.[a-z0-9.]+$/i, "");
    if (!base) return;

    if (tlds.length === 0) {
      message.warning("Tick at least one TLD first.");
      return;
    }

    const fqdns = Array.from(
      new Set(generateNameVariants(base).flatMap((name) => tlds.map((tld) => `${name}${tld}`))),
    ).slice(0, MAX_GENERATED_FQDNS);

    setIsSuggesting(true);
    setPurchaseResults(null);
    try {
      const availability = await checkAvailability(fqdns, registrarId);
      setResults(availability);
      setSelected(new Set(availability.filter((r) => r.available).map((r) => r.fqdn)));
    } catch (err) {
      message.error(err instanceof ApiError ? err.message : "Couldn't generate suggestions.");
    } finally {
      setIsSuggesting(false);
    }
  }

  function handleBrandCreated(brand: Brand) {
    setBrands((current) => [...current, brand]);
    setBrandId(brand.id);
    setIsBrandModalOpen(false);
  }

  async function handlePurchase() {
    if (!registrarId || selected.size === 0) return;
    setIsPurchasing(true);
    try {
      const outcome = await purchaseDomains({
        fqdns: Array.from(selected),
        registrar_account_id: registrarId,
        brand_id: brandId,
        region_id: regionId,
      });
      setPurchaseResults(outcome);
      const succeededDomains = outcome.filter((r) => r.success && r.domain_id);
      message.success(`${succeededDomains.length} of ${outcome.length} domains purchased.`);

      if (succeededDomains.length > 0) {
        const firstDomainId = succeededDomains[0].domain_id as number;
        modal.confirm({
          title: `${succeededDomains.length} domain${succeededDomains.length === 1 ? "" : "s"} purchased`,
          content: "Set up the first one now (server, Cloudflare, deploy), or come back to it later from the results below?",
          okText: "Set up now",
          cancelText: "I'll do this later",
          onOk: () => router.push(`/domains/${firstDomainId}`),
          className: "dms_acquire_setup_now_or_later_modal",
        });
      }
    } catch (err) {
      message.error(err instanceof ApiError ? err.message : "Purchase run failed.");
    } finally {
      setIsPurchasing(false);
    }
  }

  const columns: ColumnsType<AvailabilityResult> = [
    {
      title: "",
      render: (_, row) => (
        <Checkbox
          checked={selected.has(row.fqdn)}
          disabled={!row.available}
          onChange={(e) => {
            const next = new Set(selected);
            if (e.target.checked) next.add(row.fqdn);
            else next.delete(row.fqdn);
            setSelected(next);
          }}
        />
      ),
    },
    {
      title: "Domain",
      dataIndex: "fqdn",
      sorter: (a, b) => textCompare(a.fqdn, b.fqdn),
      sortDirections: SORT_DIRECTIONS,
      render: (v: string, row) => (
        <span className="flex items-center gap-2">
          <span className="font-mono">{v}</span>
          {row.is_premium && <Tag color="gold">Premium</Tag>}
        </span>
      ),
    },
    {
      title: "Status",
      dataIndex: "available",
      render: (v: boolean) => <Tag color={v ? "green" : "default"}>{v ? "Available" : "Taken"}</Tag>,
    },
    {
      title: "1st Year",
      dataIndex: "price_first_year",
      render: (v: number | null) => (v != null ? `$${v.toFixed(2)}` : "—"),
    },
    {
      title: "Renews",
      dataIndex: "price_renewal",
      render: (v: number | null) => (v != null ? `$${v.toFixed(2)}` : "—"),
    },
  ];

  const purchaseColumns: ColumnsType<PurchaseResult> = [
    { title: "Domain", dataIndex: "fqdn", sorter: (a, b) => textCompare(a.fqdn, b.fqdn), sortDirections: SORT_DIRECTIONS, render: (v: string) => <span className="font-mono">{v}</span> },
    {
      title: "Result",
      dataIndex: "success",
      render: (v: boolean) => <Tag color={v ? "green" : "red"}>{v ? "Purchased" : "Failed"}</Tag>,
    },
    { title: "Message", dataIndex: "message" },
    {
      title: "",
      render: (_, row) =>
        row.domain_id ? (
          <Link href={`/domains/${row.domain_id}`}>
            <Button size="small">Setup</Button>
          </Link>
        ) : null,
    },
  ];

  return (
    <div className="flex flex-col gap-4 page page-acquire">
      <div className="flex items-center gap-2">
        <Typography.Title level={4} style={{ margin: 0 }}>
          Buy Domains
        </Typography.Title>
        <PageGuide guides={BUY_DOMAINS_GUIDES} label="Buy Domains guide" />
      </div>

      <Card title="Names To Check" className="dms_acquire_names_card">
        <Input.TextArea
          rows={6}
          placeholder={"kiss918kaya\nexample.com"}
          value={namesText}
          onChange={(e) => setNamesText(e.target.value)}
        />
        <Typography.Paragraph type="secondary" style={{ marginTop: 8, marginBottom: 4 }}>
          One name or full domain per line, expanded across the ticked TLDs below. A full domain (e.g.{" "}
          <span className="font-mono">brand.com</span>) is always checked as typed too.
        </Typography.Paragraph>

        <div className="mb-3">
          <Typography.Text strong>TLDs To Check</Typography.Text>
          <Checkbox.Group value={tlds} onChange={(v) => setTlds(v as string[])} className="flex flex-wrap gap-3 mt-1">
            {TLD_OPTIONS.map((tld) => (
              <Checkbox key={tld} value={tld}>
                {tld}
              </Checkbox>
            ))}
          </Checkbox.Group>
        </div>

        <div className="flex items-center gap-3 flex-wrap">
          <Select
            style={{ width: 240 }}
            placeholder={registrars.length ? "Compare all registrars" : "No registrars configured"}
            allowClear
            value={registrarId}
            options={registrars.map((r) => ({ value: r.id, label: `${r.label} (${providerLabel(r.provider, r.provider_name)})` }))}
            onChange={setRegistrarId}
            className="dms_acquire_registrar_select"
          />
          <Button type="primary" onClick={handleCheck} loading={isChecking} className="dms_acquire_check_availability_btn">
            Check Availability
          </Button>
        </div>
        {registrars.length === 0 && (
          <Typography.Paragraph type="secondary" style={{ marginTop: 12, marginBottom: 0 }}>
            No registrar accounts yet. <Link href="/registrars">Add one</Link> to check availability and purchase
            domains.
          </Typography.Paragraph>
        )}
      </Card>

      <Card title="Suggest Name Variants" className="dms_acquire_suggest_card">
        <Typography.Paragraph type="secondary" style={{ marginTop: 0, marginBottom: 8 }}>
          Namecheap&apos;s API has no built-in suggestion tool, so this generates a small set of prefix/suffix
          variants (get-, my-, -app, -hub, etc.) for one name and checks them across the ticked TLDs above.
        </Typography.Paragraph>
        <div className="flex items-center gap-3 flex-wrap">
          <Input
            style={{ width: 240 }}
            placeholder="hariharimeow"
            value={suggestBase}
            onChange={(e) => setSuggestBase(e.target.value)}
            onPressEnter={handleGenerateSuggestions}
            className="dms_acquire_suggest_input"
          />
          <Button
            onClick={handleGenerateSuggestions}
            loading={isSuggesting}
            disabled={!suggestBase.trim()}
            className="dms_acquire_suggest_btn"
          >
            Generate & Check
          </Button>
        </div>
      </Card>

      {results.length > 0 && (
        <Card title="Results" className="dms_acquire_results_card">
          <Table<AvailabilityResult>
            rowKey="fqdn"
            columns={columns}
            dataSource={results}
            pagination={{ showSizeChanger: true, pageSizeOptions: PAGE_SIZE_OPTIONS, showTotal: (count) => `${count} results` }}
            className="dms_acquire_results_table"
          />

          <div className="flex items-center gap-3 flex-wrap mt-4">
            <Select
              allowClear
              placeholder="Project"
              style={{ width: 200 }}
              value={brandId}
              options={brands.map((b) => ({ value: b.id, label: b.name }))}
              onChange={setBrandId}
              showSearch
              optionFilterProp="label"
              className="dms_acquire_brand_select"
              popupRender={(menu) => (
                <>
                  {menu}
                  <Divider style={{ margin: "8px 0" }} />
                  <Button
                    type="text"
                    block
                    icon={<PlusOutlined />}
                    onClick={() => setIsBrandModalOpen(true)}
                    className="dms_acquire_create_brand_btn"
                  >
                    Create New Project
                  </Button>
                </>
              )}
            />
            <Select
              allowClear
              placeholder="Region"
              style={{ width: 160 }}
              value={regionId}
              options={regions.map((r) => ({ value: r.id, label: `${r.name} (${r.country_code})` }))}
              onChange={setRegionId}
              className="dms_acquire_region_select"
            />
            <Button
              type="primary"
              onClick={handlePurchase}
              loading={isPurchasing}
              disabled={selected.size === 0}
              className="dms_acquire_purchase_btn"
            >
              Purchase {selected.size > 0 ? `${selected.size} Domains` : ""}
            </Button>
          </div>
        </Card>
      )}

      {purchaseResults && (
        <Card title="Purchase Results" className="dms_acquire_purchase_results_card">
          <Table<PurchaseResult>
            rowKey="fqdn"
            columns={purchaseColumns}
            dataSource={purchaseResults}
            pagination={{ showSizeChanger: true, pageSizeOptions: PAGE_SIZE_OPTIONS, showTotal: (count) => `${count} results` }}
            className="dms_acquire_purchase_results_table"
          />
        </Card>
      )}

      <BrandFormModal open={isBrandModalOpen} onClose={() => setIsBrandModalOpen(false)} onSaved={handleBrandCreated} />
    </div>
  );
}
