import { useState, useEffect, useMemo, useRef } from 'react';
import { useNavigate, useSearchParams, useParams, useOutletContext } from 'react-router-dom';
import { Pencil } from 'lucide-react';
import { notify } from '@/lib/notify';
import { api } from '../api/client';
import { useAuth } from '../hooks/useAuth';
import type { Board } from '../api/types';
import { isHtmlEmpty } from '../utils/postContent';
import { useForumLimits } from '../hooks/useForumLimits';
import { useUnsavedChangesGuard } from '../hooks/useUnsavedChangesGuard';
import UnsavedChangesDialog from '../components/UnsavedChangesDialog';
import { serializeTags, parseTags } from '../components/TagInput';
import { Spinner } from '@/components/ui/spinner';
import ComposeHeader from '../components/compose/ComposeHeader';
import ComposeContextBar, { type PostType } from '../components/compose/ComposeContextBar';
import ComposeSpecialFields, {
  buildPollOptionsPayload,
  countValidPollOptions,
  defaultPollEndsAtLocal,
  pollEndsAtISOToLocal,
  pollEndsAtLocalToISO,
  validatePollEndsAtLocal,
} from '../components/compose/ComposeSpecialFields';
import ComposeDocument from '../components/compose/ComposeDocument';
import { sortBoardsForCompose } from '../utils/board';
import { getCachedBoards } from '../utils/layoutCache';
import type { LayoutCtx } from '../layouts/MainLayout';
import { loginPath } from '../utils/authRedirect';
import { useNoIndexSEO } from '../hooks/usePageSEO';
import { parsePermalinkID, postPath } from '../utils/permalink';
import { skipsModeration } from '../utils/userMeta';
import {
  clearComposeDraft,
  composeDraftHasContent,
  loadComposeDraft,
  saveComposeDraft,
} from '../utils/composeDraft';

interface ComposeBaseline {
  title: string;
  tags: string;
  content: string;
  boardId: string;
  postType: PostType;
}

function resolveBoards(ctxBoards?: Board[]): Board[] {
  const raw = ctxBoards && ctxBoards.length > 0 ? ctxBoards : getCachedBoards();
  return sortBoardsForCompose(raw);
}

/** 格式化剩余可编辑时间 */
function formatEditRemaining(createdAt: string, windowHours: number): string {
  if (windowHours <= 0) return '';
  const deadline = new Date(createdAt).getTime() + windowHours * 3600_000;
  const ms = deadline - Date.now();
  if (ms <= 0) return '可编辑时限已到';
  const hours = Math.floor(ms / 3600_000);
  const mins = Math.floor((ms % 3600_000) / 60_000);
  if (hours >= 24) {
    const days = Math.floor(hours / 24);
    return `还可编辑约 ${days} 天`;
  }
  if (hours > 0) return `还可编辑约 ${hours} 小时 ${mins} 分`;
  return `还可编辑约 ${mins} 分钟`;
}

export default function ComposePage() {
  const nav = useNavigate();
  const { id: editIdParam } = useParams();
  const editId = editIdParam ? parsePermalinkID(editIdParam) : null;
  const isEdit = editId !== null && !Number.isNaN(editId);
  const [params] = useSearchParams();
  const defaultBoard = params.get('board') || '';
  const { user, loading: authLoading } = useAuth();
  const { limits } = useForumLimits();
  useNoIndexSEO(isEdit ? '编辑帖子' : '发帖');
  const layoutCtx = useOutletContext<LayoutCtx | undefined>();

  const [boards, setBoards] = useState<Board[]>(() => resolveBoards(layoutCtx?.boards));
  const [boardId, setBoardId] = useState(defaultBoard);
  const [title, setTitle] = useState('');
  const [tags, setTags] = useState('');
  const [content, setContent] = useState('');
  const [postType, setPostType] = useState<PostType>('normal');
  const [pollOptions, setPollOptions] = useState(['', '']);
  const [pollMulti, setPollMulti] = useState(false);
  const [pollMaxChoices, setPollMaxChoices] = useState(2);
  const [pollEndsAt, setPollEndsAt] = useState(defaultPollEndsAtLocal);
  const [pollNoEndTime, setPollNoEndTime] = useState(false);
  const [bountyPoints, setBountyPoints] = useState(0);
  const [lotteryWinners, setLotteryWinners] = useState(1);
  const [publishing, setPublishing] = useState(false);
  const [loading, setLoading] = useState(isEdit);
  /** 新建帖：板块列表是否已就绪（避免请求中误显空态） */
  const [boardsReady, setBoardsReady] = useState(
    () => isEdit || resolveBoards(layoutCtx?.boards).length > 0,
  );
  const [baseline, setBaseline] = useState<ComposeBaseline | null>(null);
  const [editWindowHint, setEditWindowHint] = useState('');
  const [draftHint, setDraftHint] = useState('');
  /** 新建帖：是否已处理过本地草稿恢复 */
  const draftHandledRef = useRef(false);

  useEffect(() => {
    if (authLoading) return;
    if (!user) {
      nav(loginPath(isEdit ? `/post/${editId}/edit` : '/compose'));
      return;
    }

    if (isEdit) {
      setLoading(true);
      const cached = resolveBoards(layoutCtx?.boards);
      const boardsPromise = cached.length > 0
        ? Promise.resolve({ boards: cached })
        : api.boards();
      Promise.all([boardsPromise, api.post(editId!, { skipView: true })])
        .then(([boardsData, postData]) => {
          const list = sortBoardsForCompose(boardsData.boards ?? []);
          setBoards(list);
          const post = postData.post;
          const isOwnerOrAdmin = user.role === 'admin' || post.user_id === user.id;
          if (!isOwnerOrAdmin) {
            notify.error('无权编辑此帖子');
            nav(postPath(editId!, limits));
            return;
          }
          if (!postData.can_edit) {
            notify.error(postData.edit_block_reason || '当前无法编辑此帖子');
            nav(postPath(editId!, limits));
            return;
          }
          const loadedBoardId = String(post.board_id);
          const loadedType = (
            post.post_type === 'question' ? 'question'
              : post.post_type === 'poll' ? 'poll'
                : post.post_type === 'bounty' ? 'bounty'
                  : post.post_type === 'lottery' ? 'lottery'
                    : 'normal'
          ) as PostType;
          const serverBaseline: ComposeBaseline = {
            title: post.title,
            tags: post.tags ?? '',
            content: post.content ?? '',
            boardId: loadedBoardId,
            postType: loadedType,
          };
          setBoardId(loadedBoardId);
          setBaseline(serverBaseline);
          setTitle(serverBaseline.title);
          setTags(serverBaseline.tags);
          setContent(serverBaseline.content);
          setPostType(loadedType);
          if (postData.poll) {
            setPollOptions(postData.poll.options.map(o => o.text));
            setPollMulti(postData.poll.multi);
            setPollMaxChoices(postData.poll.max_choices);
            if (postData.poll.ends_at) {
              setPollNoEndTime(false);
              setPollEndsAt(pollEndsAtISOToLocal(postData.poll.ends_at));
            } else {
              setPollNoEndTime(true);
              setPollEndsAt(defaultPollEndsAtLocal());
            }
          }

          const windowHours = postData.post_edit_window_hours ?? 0;
          if (user.role !== 'admin' && windowHours > 0) {
            setEditWindowHint(formatEditRemaining(post.created_at, windowHours));
          } else {
            setEditWindowHint('');
          }
        })
        .catch((e: unknown) => {
          notify.error(e instanceof Error ? e.message : '加载帖子失败');
          nav('/');
        })
        .finally(() => setLoading(false));
      return;
    }

    const applyNewBaseline = (list: Board[], initialBoardId: string) => {
      setBoards(list);
      const boardForBaseline = defaultBoard || initialBoardId;
      const emptyBaseline: ComposeBaseline = {
        title: '',
        tags: '',
        content: '',
        boardId: boardForBaseline,
        postType: 'normal',
      };

      // 首次进入新建页：若有本地草稿则询问是否恢复
      if (!draftHandledRef.current) {
        draftHandledRef.current = true;
        const draft = loadComposeDraft();
        if (composeDraftHasContent(draft) && draft) {
          const when = new Date(draft.savedAt).toLocaleString('zh-CN', {
            month: 'numeric', day: 'numeric', hour: '2-digit', minute: '2-digit',
          });
          const restore = window.confirm(`发现 ${when} 的未发帖草稿，是否恢复？\n选「取消」将丢弃该草稿。`);
          if (restore) {
            const boardIdOk = list.some(b => String(b.id) === draft.boardId)
              ? draft.boardId
              : boardForBaseline;
            setBoardId(boardIdOk);
            setTitle(draft.title);
            setTags(draft.tags);
            setContent(draft.content);
            setPostType(draft.postType);
            if (draft.postType === 'poll') {
              if (draft.pollOptions?.length) setPollOptions(draft.pollOptions);
              if (typeof draft.pollMulti === 'boolean') setPollMulti(draft.pollMulti);
              if (typeof draft.pollMaxChoices === 'number') setPollMaxChoices(draft.pollMaxChoices);
              if (typeof draft.pollNoEndTime === 'boolean') setPollNoEndTime(draft.pollNoEndTime);
              if (typeof draft.pollEndsAt === 'string' && draft.pollEndsAt) setPollEndsAt(draft.pollEndsAt);
            }
            setBaseline(emptyBaseline);
            setDraftHint('已恢复本地草稿，编辑中将自动保存');
            return;
          }
          clearComposeDraft();
        }
      }

      setBoardId(prev => prev || boardForBaseline);
      setBaseline(emptyBaseline);
    };

    const list = resolveBoards(layoutCtx?.boards);
    if (list.length > 0) {
      const initialBoardId = defaultBoard || String(list[0].id);
      applyNewBaseline(list, initialBoardId);
      setBoardsReady(true);
      return;
    }

    setBoardsReady(false);
    api.boards().then(d => {
      const next = d.boards ?? [];
      const initialBoardId = defaultBoard || (next.length > 0 ? String(next[0].id) : '');
      applyNewBaseline(next, initialBoardId);
    }).catch(() => {
      setBoards([]);
    }).finally(() => setBoardsReady(true));
  }, [user, authLoading, nav, defaultBoard, isEdit, editId, layoutCtx?.boards, limits]);

  const isDirty = useMemo(() => {
    // 无可发帖板块时不拦截导航（空态页本身没有可保存内容）
    if (!isEdit && boards.length === 0) return false;
    if (!baseline) return false;
    return (
      title !== baseline.title
      || serializeTags(parseTags(tags)) !== serializeTags(parseTags(baseline.tags))
      || content !== baseline.content
      || boardId !== baseline.boardId
      || postType !== baseline.postType
    );
  }, [baseline, title, tags, content, boardId, postType, isEdit, boards.length]);

  // 新建帖：防抖写入本地草稿
  useEffect(() => {
    if (isEdit || !baseline) return;
    if (!isDirty) return;
    const timer = window.setTimeout(() => {
      saveComposeDraft({
        title,
        tags: serializeTags(parseTags(tags)),
        content,
        boardId,
        postType,
        ...(postType === 'poll' ? {
          pollOptions,
          pollMulti,
          pollMaxChoices,
          pollEndsAt,
          pollNoEndTime,
        } : {}),
      });
      setDraftHint('草稿已自动保存到本机');
    }, 800);
    return () => window.clearTimeout(timer);
  }, [isEdit, baseline, isDirty, title, tags, content, boardId, postType, pollOptions, pollMulti, pollMaxChoices, pollEndsAt, pollNoEndTime]);

  const {
    dialogOpen,
    stayOnPage,
    discardAndLeave: discardAndLeaveRaw,
    requestLeave,
    markSaved,
  } = useUnsavedChangesGuard({ isDirty });

  const discardAndLeave = () => {
    if (!isEdit) clearComposeDraft();
    discardAndLeaveRaw();
  };

  const leaveTo = (path: string) => {
    markSaved();
    nav(path);
  };

  if (authLoading) {
    return (
      <div className="compose-page compose-page--empty">
        <Spinner size="lg" />
      </div>
    );
  }

  if (!user) return null;

  if (loading || (!isEdit && !boardsReady)) {
    return (
      <div className="compose-page compose-page--empty">
        <Spinner size="lg" />
      </div>
    );
  }

  if (!isEdit && boards.length === 0) {
    return (
      <>
        <div className="compose-page compose-page--empty">
          <div className="compose-empty-card">
            <div className="compose-empty-icon" aria-hidden>
              <Pencil size={28} strokeWidth={1.5} />
            </div>
            <h2>暂无可发帖板块</h2>
            <p>需要管理员先创建板块后才能发布内容</p>
            {user.role === 'admin' ? (
              <button type="button" className="compose-primary-btn" onClick={() => leaveTo('/admin/boards')}>
                去创建板块
              </button>
            ) : (
              <button type="button" className="compose-ghost-btn" onClick={() => leaveTo('/')}>
                返回首页
              </button>
            )}
          </div>
        </div>
        <UnsavedChangesDialog
          open={dialogOpen}
          onStay={stayOnPage}
          onLeave={discardAndLeave}
          isEdit={isEdit}
        />
      </>
    );
  }

  const handleSubmit = async () => {
    const trimmedTitle = title.trim();
    if (!boardId) { notify.warning('请选择板块'); return; }
    if (!trimmedTitle) { notify.warning('请输入标题'); return; }
    if (isHtmlEmpty(content)) { notify.warning('请输入正文内容'); return; }
    if (postType === 'poll' && !isEdit && countValidPollOptions(pollOptions) < 2) {
      notify.warning('投票至少需要 2 个非空选项');
      return;
    }
    if (postType === 'poll' && !isEdit && !pollNoEndTime) {
      const err = validatePollEndsAtLocal(pollEndsAt);
      if (err) { notify.warning(err); return; }
    }
    if (postType === 'bounty' && !isEdit) {
      const balance = user?.points ?? 0;
      if (bountyPoints < 1) {
        notify.warning('请填写悬赏积分');
        return;
      }
      if (bountyPoints > balance) {
        notify.warning('积分不足');
        return;
      }
    }

    setPublishing(true);
    try {
      const pollEndsAtISO = postType === 'poll' && !pollNoEndTime
        ? pollEndsAtLocalToISO(pollEndsAt)
        : null;
      const payload = {
        title: trimmedTitle,
        content: content.trim(),
        tags: serializeTags(parseTags(tags)),
        board_id: boardId,
        post_type: postType,
        poll_options: postType === 'poll'
          ? buildPollOptionsPayload(pollOptions, pollMulti, pollMaxChoices, pollEndsAtISO)
          : undefined,
        bounty_points: postType === 'bounty' ? bountyPoints : undefined,
        lottery_winner_count: postType === 'lottery' ? lotteryWinners : undefined,
      };
      if (isEdit) {
        await api.updatePost(editId!, payload);
        notify.success(skipsModeration(user) ? '帖子已更新' : '已更新并重新提交审核');
        markSaved();
        nav(postPath(editId!, limits));
      } else {
        const res = await api.createPost(payload);
        clearComposeDraft();
        notify.success(res.message || (res.status === 'pending' ? '已提交审核' : '发帖成功'));
        markSaved();
        nav(postPath(res.post_id, limits));
      }
    } catch (e: unknown) {
      notify.error(e instanceof Error ? e.message : isEdit ? '保存失败' : '发帖失败');
    } finally {
      setPublishing(false);
    }
  };

  return (
    <div className="compose-page">
      <div className="compose-canvas">
        <div className="compose-shell">
          <ComposeHeader
            isEdit={isEdit}
            publishing={publishing}
            editWindowHint={editWindowHint}
            draftHint={draftHint}
            onBack={() => requestLeave(() => {
              if (isEdit) nav(postPath(editId!, limits));
              else nav(-1);
            })}
            onPublish={handleSubmit}
          />

          <div className="compose-shell-body">
            <ComposeDocument
              postType={postType}
              title={title}
              onTitleChange={setTitle}
              content={content}
              onContentChange={setContent}
              limits={limits}
            >
              <ComposeContextBar
                isEdit={isEdit}
                postType={postType}
                onPostTypeChange={setPostType}
                boards={boards}
                boardId={boardId}
                onBoardChange={setBoardId}
                tags={tags}
                onTagsChange={setTags}
                limits={limits}
              />
              <ComposeSpecialFields
                postType={postType}
                pollOptions={pollOptions}
                onPollOptionsChange={setPollOptions}
                pollMulti={pollMulti}
                onPollMultiChange={setPollMulti}
                pollMaxChoices={pollMaxChoices}
                onPollMaxChoicesChange={setPollMaxChoices}
                pollEndsAt={pollEndsAt}
                onPollEndsAtChange={setPollEndsAt}
                pollNoEndTime={pollNoEndTime}
                onPollNoEndTimeChange={setPollNoEndTime}
                bountyPoints={bountyPoints}
                onBountyPointsChange={setBountyPoints}
                userPointsBalance={!isEdit ? user?.points : undefined}
                lotteryWinners={lotteryWinners}
                onLotteryWinnersChange={setLotteryWinners}
                disabled={isEdit && postType !== 'normal' && postType !== 'question'}
              />
            </ComposeDocument>
          </div>
        </div>
      </div>
      <UnsavedChangesDialog
        open={dialogOpen}
        onStay={stayOnPage}
        onLeave={discardAndLeave}
        isEdit={isEdit}
      />
    </div>
  );
}
