"use client";

import type { Deployment, Domain, WpInstallPreview, WpIntegrityResult, WpSite } from "@domainops/shared-types";
import { ExportOutlined, InboxOutlined, ProfileOutlined, SearchOutlined, ToolOutlined, SafetyOutlined } from "@ant-design/icons";
import Link from "next/link";
import { Alert, App, Button, Card, Form, Input, Popconfirm, Progress, Radio, Table, Tabs, Tag, Typography, Upload } from "antd";
import type { ColumnsType } from "antd/es/table";
import type { UploadProps } from "antd";
import { useCallback, useEffect, useState } from "react";
import {
  deployWordPress,
  deployZip,
  listDeployments,
  rollbackDeployment,
} from "@/lib/api/deploy";
import type { DeployWordPressInput } from "@/lib/api/deploy";
import { checkWordPress, checkWordPressIntegrity, previewWordPressInstall, repairWordPress } from "@/lib/api/wordpress";
import { useAuth } from "@/lib/auth/AuthProvider";
import { WpUsersPanel } from "@/app/(dashboard)/wordpress/WpUsersPanel";
import { ApiError } from "@/lib/api/client";

const RESULT_COLORS: Record<Deployment["result"], string> = {
  success: "green",
  failed: "red",
  in_progress: "gold",
};

export function DeployPanel({
  domainId,
  fqdn,
  hasServer,
  purpose,
  wpToolkitAvailable,
  wpSite,
  onWordPressDeployed,
}: {
  domainId: number;
  fqdn: string;
  hasServer: boolean;
  purpose: Domain["purpose"];
  wpToolkitAvailable: boolean;
  wpSite: WpSite | null;
  onWordPressDeployed: () => void;
}) {
  const { message } = App.useApp();
  const [form] = Form.useForm<DeployWordPressInput>();
  const [deployments, setDeployments] = useState<Deployment[]>([]);
  const [isLoading, setIsLoading] = useState(true);
  const [isDeploying, setIsDeploying] = useState(false);
  const [uploadPercent, setUploadPercent] = useState(0);
  const [uploadName, setUploadName] = useState("");
  const [isDeployingWp, setIsDeployingWp] = useState(false);
  const [isChecking, setIsChecking] = useState(false);
  const [isPreviewing, setIsPreviewing] = useState(false);
  const [isCheckingIntegrity, setIsCheckingIntegrity] = useState(false);
  const [isRepairing, setIsRepairing] = useState(false);
  const [integrity, setIntegrity] = useState<WpIntegrityResult | null>(null);
  const { user } = useAuth();
  const can = (action: string) => !!user && (user.is_admin || (user.action_permissions ?? []).some((a) => a === "*" || a === action));
  const [preview, setPreview] = useState<WpInstallPreview | null>(null);
  const [checkNotice, setCheckNotice] = useState<{ type: "success" | "warning" | "error"; text: string } | null>(null);
  const [installNotice, setInstallNotice] = useState<{ type: "success" | "error"; text: string } | null>(null);
  const [showReinstall, setShowReinstall] = useState(false);

  const load = useCallback(async () => {
    setIsLoading(true);
    try {
      setDeployments(await listDeployments(domainId));
    } catch (err) {
      message.error(err instanceof ApiError ? err.message : "Couldn't load deploy history.");
    } finally {
      setIsLoading(false);
    }
  }, [domainId, message]);

  useEffect(() => {
    // Deploy history lives 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 load();
  }, [load]);

  const canRollback = deployments.filter((d) => d.result === "success").length > 1;

  const uploadProps: UploadProps = {
    accept: ".zip",
    multiple: false,
    showUploadList: false,
    disabled: !hasServer || isDeploying,
    customRequest: async ({ file, onSuccess, onError }) => {
      setIsDeploying(true);
      setUploadPercent(0);
      setUploadName((file as File).name);
      try {
        const deployment = await deployZip(domainId, file as File, setUploadPercent);
        if (deployment.result === "failed") {
          const failure = deployment.steps.find((step) => step.status === "failed");
          throw new ApiError(failure?.message ?? "The server could not finish the deploy.", 422, null);
        }
        message.success("Deployed.");
        onSuccess?.({});
        void load();
      } catch (err) {
        const msg = err instanceof ApiError ? err.message : "Deploy failed.";
        message.error(msg);
        onError?.(new Error(msg));
      } finally {
        setIsDeploying(false);
      }
    },
  };

  async function handleCheckWordPress() {
    setIsChecking(true);
    setCheckNotice(null);
    try {
      const result = await checkWordPress(domainId);
      setCheckNotice({ type: result.status === "not_found" ? "warning" : "success", text: result.message });
      onWordPressDeployed();
    } catch (err) {
      setCheckNotice({ type: "error", text: err instanceof ApiError ? err.message : "Couldn't check the server." });
    } finally {
      setIsChecking(false);
    }
  }

  async function handleCheckIntegrity() {
    setIsCheckingIntegrity(true);
    try {
      setIntegrity(await checkWordPressIntegrity(domainId));
    } catch (err) {
      setIntegrity(null);
      message.error(err instanceof ApiError ? err.message : "Couldn't check this WordPress.");
    } finally {
      setIsCheckingIntegrity(false);
    }
  }

  async function handleRepair() {
    setIsRepairing(true);
    try {
      await repairWordPress(domainId);
      message.success("Core files replaced - checking again...");
      await handleCheckIntegrity();
    } catch (err) {
      message.error(err instanceof ApiError ? err.message : "Couldn't repair the core files.");
    } finally {
      setIsRepairing(false);
    }
  }

  async function handlePreviewInstall() {
    setIsPreviewing(true);
    setPreview(null);
    try {
      setPreview(await previewWordPressInstall(domainId));
    } catch (err) {
      message.error(err instanceof ApiError ? err.message : "Couldn't work out the install names.");
    } finally {
      setIsPreviewing(false);
    }
  }

  const lastWpAttempt = deployments.find((d) => d.kind === "wordpress");
  const resumable = lastWpAttempt && lastWpAttempt.result === "failed" && lastWpAttempt.db_name && !lastWpAttempt.db_released_at ? lastWpAttempt : null;

  async function handleDeployWordPress(values: DeployWordPressInput) {
    setIsDeployingWp(true);
    setInstallNotice(null);
    try {
      const deployment = await deployWordPress(domainId, resumable ? values : { ...values, db_choice: undefined });
      void load();

      if (deployment.result === "failed") {
        const failure = deployment.steps.find((s) => s.status === "failed");
        setInstallNotice({ type: "error", text: failure?.message ?? "The install did not finish." });

        return;
      }

      setInstallNotice({ type: "success", text: `WordPress installed via ${wpToolkitAvailable ? "WP Toolkit" : "WP-CLI"}.` });
      form.resetFields();
      onWordPressDeployed();
    } catch (err) {
      setInstallNotice({ type: "error", text: err instanceof ApiError ? err.message : "WordPress install failed." });
    } finally {
      setIsDeployingWp(false);
    }
  }

  async function handleRollback() {
    try {
      await rollbackDeployment(domainId);
      message.success("Rolled back to the previous release.");
      void load();
    } catch (err) {
      message.error(err instanceof ApiError ? err.message : "Couldn't roll back.");
    }
  }

  const columns: ColumnsType<Deployment> = [
    { title: "Date", dataIndex: "created_at", render: (v: string) => new Date(v).toLocaleString() },
    {
      title: "Result",
      dataIndex: "result",
      render: (v: Deployment["result"]) => <Tag color={RESULT_COLORS[v]}>{v}</Tag>,
    },
    { title: "Kind", dataIndex: "kind", render: (v: Deployment["kind"]) => <Tag>{v}</Tag> },
    {
      title: "Package",
      render: (_, deployment) =>
        deployment.package_name ? <span className="font-mono">{deployment.package_name}</span> : deployment.kind === "wordpress" ? "WordPress install" : "—",
    },
    {
      title: "Database",
      render: (_, deployment) =>
        deployment.db_name ? (
          <span className="font-mono text-xs">
            {deployment.db_name}
            {deployment.table_prefix ? ` (${deployment.table_prefix})` : ""}
            {deployment.db_released_at ? " - released" : ""}
          </span>
        ) : (
          "—"
        ),
    },
    {
      title: "Type",
      render: (_, deployment) => (deployment.rollback_of ? "Rollback" : "Deploy"),
    },
  ];

  const zipPane = (
    <div className="deploy-panel__zip-pane">
      {!hasServer && (
        <p className="text-sm text-amber-600 mb-3">
          Assign this domain to a server before deploying.
        </p>
      )}
      <Upload.Dragger {...uploadProps} className="mb-4">
        <p className="ant-upload-drag-icon">
          <InboxOutlined />
        </p>
        <p className="ant-upload-text">Click or drag a ZIP file to deploy</p>
      </Upload.Dragger>

      {isDeploying && (
        <div className="mb-4 dms_domain_deploy_panel_progress" role="status">
          <div className="text-sm mb-1">
            {uploadPercent < 100 ? `Uploading ${uploadName} - ${uploadPercent}%` : "Upload complete - setting up the domain and extracting the files on the server. This can take a minute..."}
          </div>
          <Progress percent={uploadPercent} status="active" showInfo={uploadPercent < 100} />
        </div>
      )}

      <div className="flex justify-end mb-2">
        <Popconfirm
          title="Roll back to the previous release?"
          onConfirm={handleRollback}
          disabled={!canRollback}
        >
          <Button disabled={!canRollback} className="dms_domain_deploy_panel_rollback_btn">
            Roll back
          </Button>
        </Popconfirm>
      </div>
    </div>
  );

  const wordpressPane = (
    <div className="flex gap-8 flex-wrap items-start dms_domain_deploy_panel_wp_layout">
    <div className="deploy-panel__wordpress-pane" style={{ flex: "1 1 480px", minWidth: 0 }}>
      {!hasServer && (
        <p className="text-sm text-amber-600 mb-3">
          Assign this domain to a server before deploying.
        </p>
      )}

      <Typography.Paragraph type="secondary" style={{ marginBottom: 12 }}>
        Installs via {wpToolkitAvailable ? "WP Toolkit" : "WP-CLI"} on this domain&apos;s server
        {wpToolkitAvailable ? "" : " (WP Toolkit isn't licensed on that account)"}.
      </Typography.Paragraph>

      <div className="flex items-center gap-2 mb-3 flex-wrap">
        <Button onClick={() => void handleCheckWordPress()} loading={isChecking} disabled={!hasServer} icon={<SearchOutlined />} className="dms_domain_deploy_panel_wp_check_btn">
          Check for an existing WordPress
        </Button>
        <span className="text-xs opacity-70">Already installed here? It is added to the WordPress page - no setup needed.</span>
      </div>
      {checkNotice && <Alert type={checkNotice.type} showIcon title={checkNotice.text} className="mb-3 dms_domain_deploy_panel_wp_check_result" style={{ maxWidth: 500 }} />}

      <div className="flex items-center gap-2 mb-3 flex-wrap">
        <Button onClick={() => void handlePreviewInstall()} loading={isPreviewing} disabled={!hasServer} icon={<ProfileOutlined />} className="dms_domain_deploy_panel_wp_preview_btn">
          Check what a new install would use
        </Button>
        <span className="text-xs opacity-70">The database name (wp{"{n}"}) and table prefix the next install would get.</span>
      </div>
      {preview && (
        <Alert
          type={preview.replaces_existing ? "warning" : "info"}
          showIcon
          className="mb-3 dms_domain_deploy_panel_wp_preview_result"
          style={{ maxWidth: 500 }}
          title={preview.replaces_existing ? "A WordPress is already registered here - a new install would add another one." : "Next install"}
          description={
            <ul className="list-none m-0 p-0 text-sm">
              <li>Suffix: <b className="font-mono">{preview.suffix}</b></li>
              <li>Database: <b className="font-mono">{preview.db_name}</b></li>
              <li>Database user: <b className="font-mono">{preview.db_user}</b></li>
              <li>Table prefix: <b className="font-mono">{preview.table_prefix ?? "chosen by WP Toolkit"}</b></li>
              <li>Database password: generated automatically</li>
              <li>Folder: <span className="font-mono">{preview.folder}</span></li>
              <li>Installed via: {preview.method === "wp_toolkit" ? "WP Toolkit" : "WP-CLI"}</li>
              {preview.resumable && (
                <li className="mt-1">
                  An earlier attempt left <b className="font-mono">{preview.resumable.db_name}</b> behind - the install will ask whether to resume it or use {preview.db_name}.
                </li>
              )}
              <li className="mt-1 opacity-80">
                {preview.checked_server
                  ? `Checked the server: ${preview.databases_found} database${preview.databases_found === 1 ? "" : "s"} found, this is the first free number.`
                  : "Couldn't read the server's databases - the number follows the sites listed here, so check phpMyAdmin before installing."}
              </li>
            </ul>
          }
        />
      )}

      {wpSite && (
        <div className="deploy-panel__wp-site-summary mb-4">
          <Tag color="green">Already set up</Tag>
          <span className="ml-2 text-sm">
            {wpSite.admin_user ? `${wpSite.admin_user} · ` : ""}{wpSite.install_method}{wpSite.db_name ? ` · db ${wpSite.db_name}` : ""}
          </span>
          <div className="mt-2 flex gap-2 flex-wrap">
            <Link href="/wordpress">
              <Button size="small" icon={<ExportOutlined />} className="dms_domain_deploy_panel_wp_manage_link">Manage on the WordPress page</Button>
            </Link>
            <Button size="small" icon={<SafetyOutlined />} loading={isCheckingIntegrity} onClick={() => void handleCheckIntegrity()} className="dms_domain_deploy_panel_wp_integrity_btn">
              Check WordPress integrity
            </Button>
            {!showReinstall && integrity?.status !== "problem" && (
              <Button size="small" onClick={() => setShowReinstall(true)} className="dms_domain_deploy_panel_wp_reinstall_toggle">Reinstall anyway</Button>
            )}
          </div>
          {integrity && (
            <div className="mt-3 dms_domain_deploy_panel_wp_integrity_result" style={{ maxWidth: 500 }}>
              <Alert
                type={integrity.status === "ok" ? "success" : integrity.status === "warning" ? "warning" : "error"}
                showIcon
                title={integrity.status === "ok" ? "WordPress looks healthy" : integrity.status === "warning" ? "Checked, with a note" : "Something is wrong with this WordPress"}
                description={
                  <ul className="list-none m-0 p-0 text-sm">
                    {integrity.checks.map((check) => (
                      <li key={check.check}>
                        {check.status === "ok" ? "✔" : check.status === "warning" ? "!" : "✖"} <b>{check.check}</b> - {check.message}
                      </li>
                    ))}
                  </ul>
                }
              />
              {integrity.status === "problem" && (
                <div className="mt-2 flex gap-2 flex-wrap">
                  <Button size="small" type="primary" icon={<ToolOutlined />} loading={isRepairing} onClick={() => void handleRepair()} className="dms_domain_deploy_panel_wp_repair_btn">
                    Repair core files (keeps content)
                  </Button>
                  <Button size="small" danger onClick={() => setShowReinstall(true)} className="dms_domain_deploy_panel_wp_reinstall_btn">
                    Reinstall WordPress from scratch
                  </Button>
                </div>
              )}
            </div>
          )}
        </div>
      )}

      {installNotice && <Alert type={installNotice.type} showIcon title={installNotice.text} className="mb-3 dms_domain_deploy_panel_wp_install_result" style={{ maxWidth: 500, whiteSpace: "pre-wrap" }} />}

      {(!wpSite || showReinstall) && (
      <Form<DeployWordPressInput>
        form={form}
        layout="vertical"
        onFinish={handleDeployWordPress}
        disabled={!hasServer || isDeployingWp}
        className="deploy-panel__wordpress-form dms_domain_deploy_panel_wp_form"
      >
        <Form.Item
          name="admin_user"
          label="Admin username"
          rules={[{ required: true, message: "Enter an admin username" }]}
          className="dms_domain_deploy_panel_wp_admin_user_field"
        >
          <Input autoComplete="off" />
        </Form.Item>
        <Form.Item
          name="admin_password"
          label="Admin password"
          rules={[
            { required: true, message: "Enter an admin password" },
            { min: 8, message: "At least 8 characters" },
          ]}
          className="dms_domain_deploy_panel_wp_admin_password_field"
        >
          <Input.Password autoComplete="new-password" />
        </Form.Item>
        <Form.Item
          name="admin_email"
          label="Admin email"
          rules={[{ required: true, type: "email", message: "Enter a valid email" }]}
          className="dms_domain_deploy_panel_wp_admin_email_field"
        >
          <Input autoComplete="off" />
        </Form.Item>
        {resumable && (
          <Form.Item
            name="db_choice"
            label="Database"
            initialValue="resume"
            rules={[{ required: true, message: "Choose whether to resume or use a new number" }]}
            extra={`The earlier attempt stopped at: ${lastWpAttempt?.steps.find((s) => s.status === "failed")?.message ?? "an error"}`}
            className="dms_domain_deploy_panel_wp_db_choice_field"
          >
            <Radio.Group
              options={[
                { value: "resume", label: `Resume ${resumable.db_name} (prefix ${resumable.table_prefix ?? "wp_"})` },
                { value: "new", label: "Skip it and use the next free number" },
              ]}
              className="flex flex-col gap-1"
            />
          </Form.Item>
        )}
        <Form.Item style={{ marginBottom: 0 }} className="dms_domain_deploy_panel_wp_submit_field">
          <Button type="primary" htmlType="submit" loading={isDeployingWp} className="dms_domain_deploy_panel_wp_install_btn">
            {wpSite ? "Reinstall WordPress" : "Install WordPress"}
          </Button>
        </Form.Item>
      </Form>
      )}
    </div>

    <section className="deploy-panel__wp-users-section dms_domain_deploy_panel_wp_users" style={{ flex: "1 1 460px", minWidth: 0 }}>
      <h3 className="font-semibold mb-2">WordPress users</h3>
      {wpSite ? (
        <WpUsersPanel site={wpSite} fqdn={fqdn} canReveal={can("wp.reveal")} canManage={can("wp.manage")} onChanged={() => onWordPressDeployed()} />
      ) : (
        <Typography.Paragraph type="secondary">
          Users appear here once a WordPress is installed or found on this domain - use the buttons on the left.
        </Typography.Paragraph>
      )}
    </section>
    </div>
  );

  return (
    <Card title="Deploy" className="deploy-panel dms_domain_deploy_panel_card">
      <Tabs
        className="deploy-panel__tabs"
        items={[
          ...(purpose !== "wordpress" ? [{ key: "zip", label: "Static (ZIP)", children: zipPane }] : []),
          ...(purpose !== "html" ? [{ key: "wordpress", label: "WordPress", children: wordpressPane }] : []),
        ]}
      />

      <Table<Deployment>
        rowKey="id"
        size="small"
        columns={columns}
        dataSource={deployments}
        loading={isLoading}
        pagination={false}
        className="dms_domain_deploy_panel_deployments_table"
      />
    </Card>
  );
}
