"use client";

import { App, Button, Card, Popconfirm, Switch, Table, Typography } from "antd";
import { DeleteOutlined, DownloadOutlined } from "@ant-design/icons";
import { useCallback, useEffect, useState } from "react";
import { deleteDebugLog, downloadDebugLog, getGlobalSettings, listDebugLogs, updateGlobalSettings } from "@/lib/api/settings";
import type { DebugLogFile } from "@/lib/api/settings";
import { ApiError } from "@/lib/api/client";

function formatBytes(bytes: number): string {
  if (bytes < 1024) return `${bytes} B`;
  if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
  return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
}

/**
 * Admin-only, off by default - global_settings.debug_logging_enabled makes
 * every uncaught exception get written, with its full stack trace and
 * request context, into that month's storage/logs/error_log_MM-YYYY.log
 * (bootstrap/app.php's exception reporter). The table below lists every
 * monthly file so a developer can download or delete it without shell
 * access to the server.
 */
export function DeveloperCard() {
  const { message } = App.useApp();
  const [debugLoggingEnabled, setDebugLoggingEnabled] = useState(false);
  const [isLoading, setIsLoading] = useState(true);
  const [isSaving, setIsSaving] = useState(false);
  const [files, setFiles] = useState<DebugLogFile[]>([]);
  const [busyId, setBusyId] = useState<number | null>(null);

  const load = useCallback(async () => {
    setIsLoading(true);
    try {
      const [settings, logs] = await Promise.all([getGlobalSettings(), listDebugLogs()]);
      setDebugLoggingEnabled(settings.debug_logging_enabled);
      setFiles(logs);
    } catch (err) {
      message.error(err instanceof ApiError ? err.message : "Couldn't load this setting.");
    } finally {
      setIsLoading(false);
    }
  }, [message]);

  useEffect(() => {
    // eslint-disable-next-line react-hooks/set-state-in-effect
    void load();
  }, [load]);

  async function handleToggle(checked: boolean) {
    setIsSaving(true);
    try {
      const updated = await updateGlobalSettings({ debug_logging_enabled: checked });
      setDebugLoggingEnabled(updated.debug_logging_enabled);
      message.success(checked ? "Debug logging is now on." : "Debug logging is now off.");
    } catch (err) {
      message.error(err instanceof ApiError ? err.message : "Couldn't change that setting.");
    } finally {
      setIsSaving(false);
    }
  }

  async function handleDownload(file: DebugLogFile) {
    setBusyId(file.id);
    try {
      await downloadDebugLog(file);
    } catch (err) {
      message.error(err instanceof Error ? err.message : "Couldn't download the log file.");
    } finally {
      setBusyId(null);
    }
  }

  async function handleDelete(file: DebugLogFile) {
    setBusyId(file.id);
    try {
      await deleteDebugLog(file.id);
      message.success(`${file.filename} deleted.`);
      setFiles((rows) => rows.filter((row) => row.id !== file.id));
    } catch (err) {
      message.error(err instanceof ApiError ? err.message : "Couldn't delete that log file.");
    } finally {
      setBusyId(null);
    }
  }

  return (
    <Card loading={isLoading} className="dms_settings_security_developer_card">
      <div className="flex items-center gap-3 mb-4">
        <Switch checked={debugLoggingEnabled} loading={isSaving} onChange={handleToggle} />
        <div>
          <Typography.Text strong>Enable Debug Logging</Typography.Text>
          <Typography.Paragraph type="secondary" style={{ marginBottom: 0 }}>
            When on, every error is written with its full stack trace and the request that triggered it to that
            month&apos;s file (error_log_MM-YYYY.log), separate from routine logs. Leave this off unless you are
            actively reproducing a bug - it can grow quickly under real traffic.
          </Typography.Paragraph>
        </div>
      </div>

      <Table<DebugLogFile>
        size="small"
        rowKey="id"
        pagination={false}
        dataSource={files}
        locale={{ emptyText: "No log files yet." }}
        scroll={{ x: "max-content" }}
        columns={[
          { title: "File", dataIndex: "filename", render: (v: string) => <span className="font-mono">{v}</span> },
          { title: "Created on", dataIndex: "created_at", render: (v: string) => new Date(v).toLocaleString() },
          { title: "Last update", dataIndex: "updated_at", render: (v: string) => new Date(v).toLocaleString() },
          { title: "File size", dataIndex: "size_bytes", render: (v: number) => formatBytes(v) },
          {
            title: "Actions",
            render: (_, file) => (
              <div className="flex gap-2">
                <Button
                  size="small"
                  icon={<DownloadOutlined />}
                  loading={busyId === file.id}
                  onClick={() => void handleDownload(file)}
                  className="dms_settings_security_log_download_btn"
                >
                  Download
                </Button>
                <Popconfirm
                  title={`Delete ${file.filename}?`}
                  description="The file is removed from the server and can't be recovered."
                  okText="Delete"
                  okButtonProps={{ danger: true }}
                  onConfirm={() => handleDelete(file)}
                >
                  <Button size="small" danger icon={<DeleteOutlined />} className="dms_settings_security_log_delete_btn">
                    Delete
                  </Button>
                </Popconfirm>
              </div>
            ),
          },
        ]}
        className="dms_settings_security_log_table"
      />
    </Card>
  );
}
