"use client";

import type { Domain, RedirectEvent } from "@domainops/shared-types";
import { Alert, App, Button, Card, Form, Input, Popconfirm, Radio, Select, Switch, Table, Tag } from "antd";
import { useCallback, useEffect, useState } from "react";
import { createRedirect, getRedirectQuota, revertRedirect } from "@/lib/api/redirects";
import type { RedirectQuota } from "@/lib/api/redirects";
import { ApiError } from "@/lib/api/client";
import { MirrorRedirectsModal } from "./MirrorRedirectsModal";

interface RedirectFormValues {
  target_url?: string;
  path_pattern?: string;
  destination_pattern?: string;
  type: "301" | "302";
  preserve_path: boolean;
}

type RedirectKind = "domain" | "path";

const DESTINATION_PATTERN = new RegExp("^(https?://)?[a-zA-Z0-9-]+(\\.[a-zA-Z0-9-]+)+(/\\S*)?$");

const TYPE_OPTIONS = [
  { value: "301", label: "301 (Permanent)" },
  { value: "302", label: "302 (Temporary)" },
];

/**
 * Every redirect is a Cloudflare edge rule for this exact host - the domain
 * or subdomain this panel is open on - and never touches DNS records. A
 * domain gets one whole-domain redirect plus any number of path redirects.
 */
export function RedirectPanel({
  domain,
  onUpdated,
}: {
  domain: Domain;
  hasServer?: boolean;
  onUpdated: () => void;
}) {
  const { message, modal } = App.useApp();
  const [form] = Form.useForm<RedirectFormValues>();
  const [isSubmitting, setIsSubmitting] = useState(false);
  const [kind, setKind] = useState<RedirectKind>("domain");
  const [isMirrorOpen, setIsMirrorOpen] = useState(false);
  const [quota, setQuota] = useState<RedirectQuota | null>(null);
  const canUseCloudflare = !!domain.cf_zone_id;
  const hasSubdomains = (domain.subdomains?.length ?? 0) > 0;
  const activeRedirect = domain.redirect_event ?? null;
  const pathRedirects = domain.path_redirects ?? [];
  const refreshQuota = useCallback(async () => {
    if (!domain.cf_zone_id) return;
    try {
      setQuota(await getRedirectQuota(domain.id));
    } catch {
      setQuota(null);
    }
  }, [domain.id, domain.cf_zone_id]);

  useEffect(() => {
    // eslint-disable-next-line react-hooks/set-state-in-effect
    void refreshQuota();
  }, [refreshQuota, domain.redirect_event?.id, domain.path_redirects?.length]);

  function showRuleLimitDialog() {
    modal.warning({
      title: "Cloudflare redirect rules are full",
      width: 520,
      content: (
        <div className="flex flex-col gap-2">
          <p>
            This domain&apos;s Cloudflare zone has used {quota ? `${quota.used} of its ${quota.limit ?? "?"}` : "all of its"} redirect rules
            {quota?.plan ? ` (${quota.plan} plan)` : ""}, so a new redirect can&apos;t be added.
          </p>
          <p>To make room, you can:</p>
          <ul className="list-disc pl-5">
            <li>Revert redirects you no longer need (Redirects page, or each domain&apos;s Redirect tab).</li>
            <li>Combine several paths into one prefix rule such as /old/*.</li>
            <li>Upgrade the Cloudflare plan for this zone for a larger allowance.</li>
          </ul>
        </div>
      ),
      okText: "Understood",
    });
  }

  const isDomainKindBlocked = kind === "domain" && !!activeRedirect;

  async function handleCreate(values: RedirectFormValues) {
    setIsSubmitting(true);
    try {
      const result = await createRedirect(
        domain.id,
        kind === "path"
          ? { path_pattern: values.path_pattern, destination_pattern: values.destination_pattern, type: values.type }
          : { target_url: values.target_url, type: values.type, preserve_path: values.preserve_path },
      );
      message.success("Redirect created on Cloudflare.");
      if (result.proxy_warning) message.warning(result.proxy_warning, 8);
      form.resetFields();
      onUpdated();
    } catch (err) {
      if (err instanceof ApiError && err.errors?.rule_limit) {
        void refreshQuota();
        showRuleLimitDialog();
        return;
      }
      message.error(err instanceof ApiError ? err.message : "Couldn't create that redirect.");
    } finally {
      setIsSubmitting(false);
    }
  }

  async function handleRevert(redirect: RedirectEvent) {
    setIsSubmitting(true);
    try {
      await revertRedirect(domain.id, redirect.path_pattern ? redirect.id : undefined);
      message.success("Redirect reverted.");
      onUpdated();
    } catch (err) {
      message.error(err instanceof ApiError ? err.message : "Couldn't revert that redirect.");
    } finally {
      setIsSubmitting(false);
    }
  }

  const mirrorButton = hasSubdomains ? (
    <Button onClick={() => setIsMirrorOpen(true)} className="dms_domain_redirect_panel_mirror_btn">
      Mirror subdomains
    </Button>
  ) : null;

  return (
    <div className="flex flex-col gap-4 dms_domain_redirect_panel">
      {activeRedirect && (
        <Card title="Domain Redirect" extra={mirrorButton} className="dms_domain_redirect_panel_active_card">
          <p className="mb-2">
            <span className="font-mono">{domain.fqdn}</span> redirects (<Tag>{activeRedirect.type}</Tag>) to{" "}
            <span className="font-mono">{activeRedirect.target_url}</span>
          </p>
          <p className="text-sm text-gray-500 mb-4">
            {activeRedirect.preserve_path ? "Keeps the page path (/about goes to /about)." : "Sends every page to the target's home page."}{" "}
            Set on Cloudflare - DNS records are untouched.
          </p>
          <Popconfirm title="Revert this redirect?" onConfirm={() => handleRevert(activeRedirect)}>
            <Button danger loading={isSubmitting} className="dms_domain_redirect_panel_revert_btn">
              Revert
            </Button>
          </Popconfirm>
        </Card>
      )}

      {pathRedirects.length > 0 && (
        <Card title="Path Redirects" className="dms_domain_redirect_panel_paths_card">
          <Table<RedirectEvent>
            size="small"
            rowKey="id"
            pagination={false}
            dataSource={pathRedirects}
            columns={[
              { title: "From Path", dataIndex: "path_pattern", render: (v: string) => <span className="font-mono">{v}</span> },
              { title: "To", dataIndex: "target_url", render: (_, row) => <span className="font-mono">{row.destination_pattern}</span> },
              { title: "Type", dataIndex: "type", render: (v: string) => <Tag>{v}</Tag> },
              {
                title: "",
                render: (_, row) => (
                  <Popconfirm title="Revert this path redirect?" onConfirm={() => handleRevert(row)}>
                    <Button size="small" danger className="dms_domain_redirect_panel_path_revert_btn">
                      Revert
                    </Button>
                  </Popconfirm>
                ),
              },
            ]}
            className="dms_domain_redirect_panel_paths_table"
          />
        </Card>
      )}

      <Card title="Add Redirect" extra={activeRedirect ? null : mirrorButton} className="dms_domain_redirect_panel_card">
        {!canUseCloudflare && (
          <p className="text-sm text-amber-600 mb-3">
            Provision this domain with Cloudflare first (Setup tab) - every redirect is set on Cloudflare.
          </p>
        )}
        {quota && quota.limit !== null && (
          <Alert
            type={quota.used >= quota.limit ? "error" : quota.used >= quota.limit * 0.8 ? "warning" : "info"}
            showIcon
            className="mb-3 dms_domain_redirect_panel_quota_alert"
            title={`Cloudflare redirect rules used: ${quota.used} of ${quota.limit}${quota.plan ? ` (${quota.plan})` : ""}`}
            description={quota.used >= quota.limit ? "The allowance is full - revert a redirect or upgrade the plan before adding another." : undefined}
          />
        )}
        <Form.Item label="Redirect" className="dms_domain_redirect_panel_kind_field">
          <Radio.Group value={kind} onChange={(e) => setKind(e.target.value)}>
            <Radio.Button value="domain">Whole {domain.parent_domain_id ? "subdomain" : "domain"}</Radio.Button>
            <Radio.Button value="path">Specific path</Radio.Button>
          </Radio.Group>
        </Form.Item>
        {isDomainKindBlocked && (
          <p className="text-sm text-gray-500 mb-3">
            {domain.fqdn} already has a domain redirect - creating another one replaces it.
          </p>
        )}
        <Form<RedirectFormValues>
          form={form}
          layout="vertical"
          onFinish={handleCreate}
          disabled={!canUseCloudflare || isSubmitting}
          initialValues={{ type: "301", preserve_path: true }}
          className="dms_domain_redirect_panel_form"
        >
          {kind === "path" ? (
            <>
              <Alert
                type="info"
                showIcon
                className="mb-3 dms_domain_redirect_panel_path_note"
                title="Paths are matched exactly as typed"
                description="/about-us/ and /about-us are different pages, and capital letters count - add each version you need. A path ending in /* matches everything under it."
              />
              <Form.Item
                name="path_pattern"
                label="From path"
                rules={[{ required: true, message: "Enter the path to redirect" }]}
                extra={`A path starting with "/" - e.g. "/blog" for just that page, or "/old/*" for everything under /old/.`}
                className="dms_domain_redirect_panel_path_pattern_field"
              >
                <Input placeholder="/old/*" />
              </Form.Item>
              <Form.Item
                name="destination_pattern"
                label="To"
                rules={[{ required: true, message: "Enter the destination" }]}
                extra={`No "https://" - it is added automatically. Use "$1" for what the "*" matched, e.g. "newsite.com/$1".`}
                className="dms_domain_redirect_panel_destination_pattern_field"
              >
                <Input placeholder="newsite.com/$1" />
              </Form.Item>
            </>
          ) : (
            <Form.Item
              name="target_url"
              label="Redirect to"
              rules={[
                { required: true, message: "Enter the destination domain" },
                { pattern: DESTINATION_PATTERN, message: "Enter a domain like newsite.com" },
              ]}
              extra="Just the domain - https:// is added automatically."
              className="dms_domain_redirect_panel_target_url_field"
            >
              <Input placeholder="newsite.com" />
            </Form.Item>
          )}
          <Form.Item name="type" label="Type" className="dms_domain_redirect_panel_type_field">
            <Select options={TYPE_OPTIONS} />
          </Form.Item>
          {kind === "domain" && (
            <Form.Item
              name="preserve_path"
              label="Keep the page path"
              valuePropName="checked"
              extra="On: /about on this domain goes to /about on the target. Off: every page goes to the target's home page."
              className="dms_domain_redirect_panel_preserve_path_field"
            >
              <Switch />
            </Form.Item>
          )}
          <Button type="primary" htmlType="submit" loading={isSubmitting} disabled={!canUseCloudflare} className="dms_domain_redirect_panel_create_btn">
            Create redirect
          </Button>
        </Form>
      </Card>

      {hasSubdomains && (
        <MirrorRedirectsModal
          open={isMirrorOpen}
          domain={domain}
          onClose={() => setIsMirrorOpen(false)}
          onDone={() => {
            setIsMirrorOpen(false);
            onUpdated();
          }}
        />
      )}
    </div>
  );
}
