import { useEffect, useMemo, useRef, useState, type ReactNode } from 'react';
import { useNavigate } from 'react-router-dom';
import {
  Alert,
  Button,
  Card,
  Collapse,
  Form,
  Input,
  InputNumber,
  Modal,
  Space,
  Switch,
  Table,
  Tag,
  Typography,
  message,
} from 'antd';
import type { MockSmsCodeItem, SystemConfigFieldMeta, SystemConfigFormResponse } from '@dukang/shared-types';
import { request, type HqProfile } from '../lib/api';
import { ConfigImageField, ConfigImageListField } from '../components/ConfigMediaFields';

const { TextArea } = Input;

function MockSmsCodePanel({ codes, loading }: { codes: MockSmsCodeItem[]; loading?: boolean }) {
  return (
    <div style={{ marginTop: -8, marginBottom: 16, marginLeft: 0 }}>
      <Typography.Text type="secondary" style={{ display: 'block', marginBottom: 8 }}>
        最近 Mock 验证码（写入数据库，最新 50 条）
      </Typography.Text>
      <Table<MockSmsCodeItem>
        size="small"
        rowKey="id"
        loading={loading}
        pagination={false}
        scroll={{ y: 240 }}
        locale={{ emptyText: '暂无记录，触发短信发送后将显示在此' }}
        columns={[
          {
            title: '时间',
            dataIndex: 'createdAt',
            width: 168,
            render: (v: string) => new Date(v).toLocaleString(),
          },
          { title: '手机号', dataIndex: 'phone', width: 120 },
          { title: '场景', dataIndex: 'scene', width: 160 },
          {
            title: '验证码',
            dataIndex: 'code',
            width: 88,
            render: (code: string) => (
              <Typography.Text copyable strong>
                {code}
              </Typography.Text>
            ),
          },
        ]}
        dataSource={codes}
      />
    </div>
  );
}

function renderField(
  field: SystemConfigFieldMeta,
  configuredSecrets: string[],
  extra?: ReactNode,
) {
  const isConfiguredSecret = field.secret && configuredSecrets.includes(field.key);

  if (field.type === 'boolean') {
    return (
      <div key={field.key}>
        <Form.Item
          name={field.key}
          label={
            <Space size={4}>
              <span>{field.label}</span>
              <Typography.Text type="secondary" code style={{ fontSize: 11 }}>
                {field.key}
              </Typography.Text>
              {field.requiresRestart ? <Tag color="orange">需重启</Tag> : <Tag color="green">即时</Tag>}
            </Space>
          }
          tooltip={field.description}
          valuePropName="checked"
          getValueFromEvent={(checked: boolean) => (checked ? 'true' : 'false')}
          getValueProps={(v: string) => ({ checked: v === 'true' || v === '1' })}
        >
          <Switch />
        </Form.Item>
        {extra}
      </div>
    );
  }

  if (field.type === 'image' || field.type === 'imageList') {
    return (
      <Form.Item
        key={field.key}
        name={field.key}
        label={
          <Space size={4} wrap>
            <span>{field.label}</span>
            <Typography.Text type="secondary" code style={{ fontSize: 11 }}>
              {field.key}
            </Typography.Text>
            {field.requiresRestart ? <Tag color="orange">需重启</Tag> : <Tag color="green">即时</Tag>}
          </Space>
        }
        tooltip={field.description}
        trigger="onChange"
        getValueFromEvent={(v: unknown) => (typeof v === 'string' ? v : '')}
        normalize={(v) => (typeof v === 'string' ? v : '')}
      >
        {field.type === 'image' ? (
          <ConfigImageField bizType="footer" />
        ) : (
          <ConfigImageListField bizType="swiper" />
        )}
      </Form.Item>
    );
  }

  const input =
    field.type === 'textarea' ? (
      <TextArea rows={3} placeholder={field.placeholder} />
    ) : field.type === 'number' ? (
      <InputNumber style={{ width: '100%' }} placeholder={field.placeholder} />
    ) : field.secret ? (
      <Input.Password
        placeholder={isConfiguredSecret ? '已配置，留空则不修改' : field.placeholder}
        autoComplete="new-password"
      />
    ) : (
      <Input placeholder={field.placeholder} />
    );

  return (
    <Form.Item
      key={field.key}
      name={field.key}
      label={
        <Space size={4} wrap>
          <span>{field.label}</span>
          <Typography.Text type="secondary" code style={{ fontSize: 11 }}>
            {field.key}
          </Typography.Text>
          {field.requiresRestart ? <Tag color="orange">需重启</Tag> : <Tag color="green">即时</Tag>}
          {isConfiguredSecret ? <Tag>已配置</Tag> : null}
        </Space>
      }
      tooltip={field.description}
    >
      {input}
    </Form.Item>
  );
}

export default function SystemSettingsPage() {
  const navigate = useNavigate();
  const [form] = Form.useForm<Record<string, string>>();
  const [meta, setMeta] = useState<SystemConfigFormResponse | null>(null);
  const [profile, setProfile] = useState<HqProfile | null>(null);
  const [loading, setLoading] = useState(true);
  const [saving, setSaving] = useState(false);
  const [syncing, setSyncing] = useState(false);
  const [dirty, setDirty] = useState(false);
  const dirtyRef = useRef(false);
  const bypassLeaveRef = useRef(false);
  const mockSmsEnabled = Form.useWatch('MOCK_SMS', form) === 'true';

  dirtyRef.current = dirty;

  async function load(silent = false) {
    if (!silent) setLoading(true);
    try {
      const data = await request<SystemConfigFormResponse>('/admin/system-config');
      if (silent) {
        setMeta((prev) =>
          prev
            ? { ...prev, mockSmsCodes: data.mockSmsCodes, updatedAt: data.updatedAt }
            : data,
        );
        return;
      }
      setMeta(data);
      form.setFieldsValue(data.values);
      setDirty(false);
    } catch (e) {
      if (!silent) message.error(e instanceof Error ? e.message : '加载失败');
    } finally {
      if (!silent) setLoading(false);
    }
  }

  useEffect(() => {
    void load();
    request<HqProfile>('/admin/auth/me').then(setProfile).catch(() => {});
  }, []);

  useEffect(() => {
    if (!mockSmsEnabled) return;
    const timer = window.setInterval(() => void load(true), 5000);
    return () => window.clearInterval(timer);
  }, [mockSmsEnabled]);

  useEffect(() => {
    const onBeforeUnload = (e: BeforeUnloadEvent) => {
      if (!dirtyRef.current) return;
      e.preventDefault();
      e.returnValue = '';
    };
    window.addEventListener('beforeunload', onBeforeUnload);
    return () => window.removeEventListener('beforeunload', onBeforeUnload);
  }, []);

  useEffect(() => {
    const onDocClick = (e: MouseEvent) => {
      if (!dirtyRef.current || bypassLeaveRef.current) return;
      const target = e.target as HTMLElement | null;
      const anchor = target?.closest?.('a');
      if (!anchor || !(anchor instanceof HTMLAnchorElement)) return;
      if (anchor.target === '_blank' || anchor.hasAttribute('download')) return;
      const url = new URL(anchor.href, window.location.href);
      if (url.origin !== window.location.origin) return;
      if (url.pathname === window.location.pathname && url.search === window.location.search) return;

      e.preventDefault();
      e.stopPropagation();
      Modal.confirm({
        title: '有未保存的更改',
        content: '离开前请先保存，否则更改将丢失。',
        okText: '仍要离开',
        cancelText: '留下',
        onOk: () => {
          bypassLeaveRef.current = true;
          setDirty(false);
          navigate(`${url.pathname}${url.search}${url.hash}`);
          window.setTimeout(() => {
            bypassLeaveRef.current = false;
          }, 0);
        },
      });
    };
    document.addEventListener('click', onDocClick, true);
    return () => document.removeEventListener('click', onDocClick, true);
  }, [navigate]);

  const collapseItems = useMemo(() => {
    if (!meta) return [];
    return meta.groups.map((group) => ({
      key: group.key,
      label: group.label,
      forceRender: true,
      children: (
        <div style={{ maxWidth: 720 }}>
          {meta.fields
            .filter((f) => f.group === group.key)
            .map((f) =>
              renderField(
                f,
                meta.configuredSecrets,
                f.key === 'MOCK_SMS' && mockSmsEnabled ? (
                  <MockSmsCodePanel codes={meta.mockSmsCodes ?? []} loading={loading} />
                ) : undefined,
              ),
            )}
        </div>
      ),
    }));
  }, [meta, mockSmsEnabled, loading]);

  async function onSave() {
    await form.validateFields();
    const values = form.getFieldsValue(true);
    const payload: Record<string, string> = {};
    for (const [k, v] of Object.entries(values)) {
      payload[k] = v === undefined || v === null ? '' : String(v);
    }
    setSaving(true);
    try {
      const res = await request<{ updatedKeys: string[]; requiresRestartKeys: string[] }>(
        '/admin/system-config',
        { method: 'PUT', body: JSON.stringify({ values: payload }) },
      );
      message.success(`已保存 ${res.updatedKeys.length} 项`);
      if (res.requiresRestartKeys.length) {
        message.warning(`以下配置需重启 API 后生效：${res.requiresRestartKeys.join(', ')}`);
      }
      setDirty(false);
      await load();
    } catch (e) {
      message.error(e instanceof Error ? e.message : '保存失败');
    } finally {
      setSaving(false);
    }
  }

  async function onSyncEnv() {
    setSyncing(true);
    try {
      const res = await request<{ message: string; envFilePath: string }>(
        '/admin/system-config/sync-env',
        { method: 'POST' },
      );
      message.success(res.message || '已同步到 env 文件');
    } catch (e) {
      message.error(e instanceof Error ? e.message : '同步失败');
    } finally {
      setSyncing(false);
    }
  }

  async function onImportEnv() {
    try {
      const res = await request<{ imported: number }>('/admin/system-config/import-env', {
        method: 'POST',
      });
      message.success(`已从当前进程环境导入 ${res.imported} 项`);
      await load();
    } catch (e) {
      message.error(e instanceof Error ? e.message : '导入失败');
    }
  }

  return (
    <div style={{ paddingBottom: 88 }}>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', marginBottom: 16 }}>
        <div>
          <Typography.Title level={4} style={{ margin: 0 }}>
            系统设置
          </Typography.Title>
          <Typography.Paragraph type="secondary" style={{ marginBottom: 0, marginTop: 8 }}>
            配置存于 <code>system_config</code> 表；保存后写入进程环境。可同步到{' '}
            <code>{meta?.envFilePath ?? '.env'}</code> 以便部署持久化。
          </Typography.Paragraph>
        </div>
        <Space>
          {profile?.adminRole === 'SUPER_ADMIN' ? (
            <>
              <Button onClick={() => void onImportEnv()}>从环境导入</Button>
              <Button loading={syncing} onClick={() => void onSyncEnv()}>
                同步到 env 文件
              </Button>
            </>
          ) : null}
        </Space>
      </div>

      <Alert
        type="info"
        showIcon
        style={{ marginBottom: 16 }}
        message="生效说明"
        description={
          <ul style={{ margin: '8px 0 0', paddingLeft: 20 }}>
            <li>
              <Tag color="green">即时</Tag>：保存后写入 <code>process.env</code>，Mock 开关、短信模板等可立即生效。
            </li>
            <li>
              <Tag color="orange">需重启</Tag>：微信/OSS 密钥等集成凭证变更后，<strong>建议重启 API 进程</strong>。
            </li>
            <li>OSS 始终走阿里云配置；凭证缺失时上传接口将直接报错。</li>
            <li>修改后请点击右下角「保存」；未保存离开页面将提示确认。</li>
          </ul>
        }
      />

      <Card loading={loading}>
        <Form form={form} layout="vertical" onValuesChange={() => setDirty(true)}>
          <Collapse defaultActiveKey={[]} items={collapseItems} />
        </Form>
        {meta?.updatedAt ? (
          <Typography.Text type="secondary" style={{ display: 'block', marginTop: 16 }}>
            最近更新：{new Date(meta.updatedAt).toLocaleString()}
          </Typography.Text>
        ) : null}
      </Card>

      <div
        style={{
          position: 'fixed',
          right: 32,
          bottom: 32,
          zIndex: 1000,
          display: 'flex',
          flexDirection: 'column',
          alignItems: 'flex-end',
          gap: 8,
        }}
      >
        {dirty ? <Tag color="orange">有未保存更改</Tag> : null}
        <Button
          type="primary"
          size="large"
          loading={saving}
          onClick={() => void onSave()}
          style={{
            minWidth: 120,
            boxShadow: '0 6px 16px rgba(0,0,0,0.18)',
          }}
        >
          保存
        </Button>
      </div>
    </div>
  );
}
