import { useIsMobile } from '@/hooks/useIsMobile';
import AddTimeLineItemModal from '@/pages/story/components/AddTimeLineItemModal';
import SortableTimelineGrid from '@/pages/story/components/SortableTimelineGrid';
import TimelineItemDrawer from '@/pages/story/components/TimelineItemDrawer';
import type { StoryItem, StoryItemTimeQueryParams, StoryType } from '@/pages/story/data';
import { queryStoryDetail, queryStoryItem, removeStoryItem } from '@/pages/story/service';
import {
  getStoryShareActionLabel,
  getStorySharePath,
  getStoryShareState,
  getStoryShareStatusText,
} from '@/pages/story/utils/shareState';
import { judgePermission } from '@/pages/story/utils/utils';
import {
  EyeOutlined,
  MoreOutlined,
  PlusOutlined,
  ShareAltOutlined,
  SyncOutlined,
  TeamOutlined,
  VerticalAlignTopOutlined,
} from '@ant-design/icons';
import { PageContainer } from '@ant-design/pro-components';
import { history, useParams, useRequest } from '@umijs/max';
import { Button, Dropdown, Empty, FloatButton, message, Space, Spin, Tag } from 'antd';
import type { MenuProps } from 'antd';
import { PullToRefresh } from 'antd-mobile';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import './detail.css';
import CollaboratorModal from './components/CollaboratorModal';

type QueryParams = StoryItemTimeQueryParams & { current?: number; pageSize?: number };
type LoadDirection = 'init' | 'older' | 'newer' | 'refresh';

const normalizeStoryDetailResponse = (response: any): StoryType | undefined => {
  if (!response) return undefined;
  if (response?.data) return response.data as StoryType;
  return response as StoryType;
};

const normalizeStoryItemsResponse = (response: any): StoryItem[] => {
  if (Array.isArray(response?.list)) return response.list as StoryItem[];
  if (Array.isArray(response?.data?.list)) return response.data.list as StoryItem[];
  if (Array.isArray(response?.data)) return response.data as StoryItem[];
  return [];
};

const normalizeTimeParam = (value: StoryItem['storyItemTime'] | undefined): string | undefined => {
  if (!value) return undefined;

  if (Array.isArray(value)) {
    const [year, month, day, hour = 0, minute = 0, second = 0] = value;
    return `${year}-${String(month).padStart(2, '0')}-${String(day).padStart(2, '0')} ${String(hour).padStart(2, '0')}:${String(minute).padStart(2, '0')}:${String(second).padStart(2, '0')}`;
  }

  return String(value);
};

const getTimeValue = (time: StoryItem['storyItemTime'] | undefined): number => {
  if (!time) return 0;

  if (Array.isArray(time)) {
    const [year, month, day, hour = 0, minute = 0, second = 0] = time;
    return new Date(year, month - 1, day, hour, minute, second).getTime();
  }

  return new Date(String(time)).getTime();
};

const getDateKey = (time: StoryItem['storyItemTime'] | undefined): { label: string; sortValue: number } => {
  if (Array.isArray(time)) {
    const [year, month, day] = time;
    return {
      label: `${year}-${String(month).padStart(2, '0')}-${String(day).padStart(2, '0')}`,
      sortValue: new Date(year, month - 1, day).getTime(),
    };
  }

  if (time) {
    const datePart = String(time).split(' ')[0];
    return {
      label: datePart,
      sortValue: new Date(datePart).getTime(),
    };
  }

  return {
    label: 'Undated',
    sortValue: 0,
  };
};

const groupItemsByDate = (items: StoryItem[]) => {
  const groups: Record<string, { dateKey: string; items: StoryItem[]; sortValue: number }> = {};

  items.forEach((item) => {
    const { label, sortValue } = getDateKey(item.storyItemTime);

    if (!groups[label]) {
      groups[label] = { dateKey: label, items: [], sortValue };
    }

    groups[label].items.push(item);
  });

  Object.values(groups).forEach((group) => {
    group.items.sort((a, b) => getTimeValue(a.storyItemTime) - getTimeValue(b.storyItemTime));
  });

  return groups;
};

const mergeGroupOrder = (items: StoryItem[], dateKey: string, newItems: StoryItem[]) => {
  const groups = groupItemsByDate(items);

  if (!groups[dateKey]) {
    return items;
  }

  groups[dateKey].items = newItems;

  return Object.values(groups)
    .sort((a, b) => b.sortValue - a.sortValue)
    .flatMap((group) => group.items);
};

const Index = () => {
  const isMobile = useIsMobile();
  const { id: lineId } = useParams<{ id: string }>();
  const containerRef = useRef<HTMLDivElement>(null);
  const [items, setItems] = useState<StoryItem[]>([]);
  const [loading, setLoading] = useState(false);
  const [hasMoreOld, setHasMoreOld] = useState(true);
  const [hasMoreNew, setHasMoreNew] = useState(true);
  const [openAddItemModal, setOpenAddItemModal] = useState(false);
  const [currentItem, setCurrentItem] = useState<StoryItem>();
  const [currentOption, setCurrentOption] = useState<'add' | 'edit' | 'addSubItem' | 'editSubItem'>();
  const [openDetailDrawer, setOpenDetailDrawer] = useState(false);
  const [openCollaboratorModal, setOpenCollaboratorModal] = useState(false);
  const [detailItem, setDetailItem] = useState<StoryItem>();
  const [pagination, setPagination] = useState({ current: 1, pageSize: 30 });
  const [isRefreshing, setIsRefreshing] = useState(false);
  const [showScrollTop, setShowScrollTop] = useState(false);
  const [currentTimeArr, setCurrentTimeArr] = useState<
    [StoryItem['storyItemTime'] | undefined, StoryItem['storyItemTime'] | undefined]
  >([undefined, undefined]);
  const [loadDirection, setLoadDirection] = useState<LoadDirection>('init');
  const hasShownNoMoreOldRef = useRef(false);
  const hasShownNoMoreNewRef = useRef(false);

  const { data: response, run } = useRequest(
    (params?: QueryParams) =>
      queryStoryItem({ storyInstanceId: lineId, pageSize: pagination.pageSize, ...params }),
    { manual: true },
  );

  const { data: detailResponse, run: queryDetail, loading: queryDetailLoading } = useRequest(
    () => queryStoryDetail(lineId ?? ''),
  );

  const detail = normalizeStoryDetailResponse(detailResponse);
  const storyId = lineId || detail?.instanceId;
  const canEdit = judgePermission(detail?.permissionType ?? null, 'edit');
  const canManageCollaborators = judgePermission(detail?.permissionType ?? null, 'auth');
  const shareState = getStoryShareState(detail);
  const shareStatusText = getStoryShareStatusText(detail);
  const shareActionLabel =
    shareState === 'public' ? 'Open Public Share' : getStoryShareActionLabel(detail);

  const refreshStory = useCallback(() => {
    setItems([]);
    setPagination({ current: 1, pageSize: 30 });
    setLoadDirection('refresh');
    setLoading(true);
    setIsRefreshing(false);
    setHasMoreOld(true);
    setHasMoreNew(true);
    hasShownNoMoreOldRef.current = false;
    hasShownNoMoreNewRef.current = false;
    void run({ current: 1 });
    void queryDetail();
  }, [queryDetail, run]);

  const openSharePreview = useCallback(() => {
    if (!storyId) {
      message.warning('This story is not ready for preview yet.');
      return;
    }

    history.push(`/share/preview/${storyId}`);
  }, [storyId]);

  const openShareStudio = useCallback(() => {
    if (!storyId) {
      message.warning('This story is not ready for Share Studio yet.');
      return;
    }

    history.push(`/share/studio/${storyId}`);
  }, [storyId]);

  const openShareSurface = useCallback(() => {
    const sharePath = getStorySharePath(detail);
    if (!sharePath) {
      message.warning('This story is not ready for sharing yet.');
      return;
    }

    history.push(sharePath);
  }, [detail]);

  useEffect(() => {
    setItems([]);
    setHasMoreOld(true);
    setHasMoreNew(true);
    setLoadDirection('init');
    setLoading(true);
    setIsRefreshing(false);
    hasShownNoMoreOldRef.current = false;
    hasShownNoMoreNewRef.current = false;
    void queryDetail();
    void run();
  }, [lineId, queryDetail, run]);

  useEffect(() => {
    if (!response) return;

    const fetched = normalizeStoryItemsResponse(response);
    const pageSize = pagination.pageSize;
    const noMore = fetched.length < pageSize;

    if (!fetched.length) {
      if (loadDirection === 'older') {
        setHasMoreOld(false);
      } else if (loadDirection === 'newer') {
        setHasMoreNew(false);
      } else {
        setItems([]);
      }

      setLoading(false);
      setIsRefreshing(false);
      setLoadDirection('init');
      return;
    }

    if (loadDirection === 'older') {
      setItems((prev) => {
        const next = [...prev, ...fetched];
        setCurrentTimeArr([next[0]?.storyItemTime, next[next.length - 1]?.storyItemTime]);
        return next;
      });
      setHasMoreOld(!noMore);

      if (noMore && !hasShownNoMoreOldRef.current) {
        hasShownNoMoreOldRef.current = true;
        message.info('No older moments left.');
      }
    } else if (loadDirection === 'newer') {
      setItems((prev) => {
        const next = [...fetched, ...prev];
        setCurrentTimeArr([next[0]?.storyItemTime, next[next.length - 1]?.storyItemTime]);
        return next;
      });
      setHasMoreNew(!noMore);

      if (noMore && !hasShownNoMoreNewRef.current) {
        hasShownNoMoreNewRef.current = true;
        message.info('No newer moments found.');
      }
    } else {
      setItems(fetched);
      setCurrentTimeArr([fetched[0]?.storyItemTime, fetched[fetched.length - 1]?.storyItemTime]);
      setHasMoreOld(!noMore);
      setHasMoreNew(true);
    }

    setLoading(false);
    setIsRefreshing(false);
    setLoadDirection('init');
  }, [loadDirection, pagination.pageSize, response]);

  const loadOlder = useCallback(() => {
    if (loading || !hasMoreOld) {
      return;
    }

    const beforeTime = items[items.length - 1]?.storyItemTime || currentTimeArr[1];
    if (!beforeTime) return;

    const nextPage = pagination.current + 1;
    setPagination((prev) => ({ ...prev, current: nextPage }));
    setLoadDirection('older');
    setLoading(true);
    void run({ current: nextPage, beforeTime: normalizeTimeParam(beforeTime) });
  }, [currentTimeArr, hasMoreOld, items, loading, pagination.current, run]);

  const loadNewer = useCallback(() => {
    if (loading || !hasMoreNew || isRefreshing) {
      return;
    }

    const afterTime = items[0]?.storyItemTime || currentTimeArr[0];
    if (!afterTime) return;

    setPagination((prev) => ({ ...prev, current: 1 }));
    setLoadDirection('newer');
    setIsRefreshing(true);
    setLoading(true);
    void run({ current: 1, afterTime: normalizeTimeParam(afterTime) });
  }, [currentTimeArr, hasMoreNew, isRefreshing, items, loading, run]);

  useEffect(() => {
    const container = containerRef.current;
    if (!container) return;

    const handleScroll = () => {
      const { scrollTop, scrollHeight, clientHeight } = container;
      setShowScrollTop(scrollTop > 300);

      if (scrollHeight - scrollTop - clientHeight < 200) {
        loadOlder();
      }
    };

    container.addEventListener('scroll', handleScroll);
    return () => container.removeEventListener('scroll', handleScroll);
  }, [loadOlder]);

  const handleRefresh = useCallback(() => {
    if (isRefreshing) return;

    setIsRefreshing(true);
    setLoadDirection('refresh');
    setPagination((prev) => ({ ...prev, current: 1 }));
    hasShownNoMoreOldRef.current = false;
    hasShownNoMoreNewRef.current = false;
    setLoading(true);
    void run({ current: 1 });
    void queryDetail();
  }, [isRefreshing, queryDetail, run]);

  const scrollToTop = () => {
    containerRef.current?.scrollTo({ top: 0, behavior: 'smooth' });
  };

  const groupedItems = useMemo(() => groupItemsByDate(items), [items]);

  const summaryDescription =
    detail?.description ||
    'Build the timeline, curate a cover, and turn the story into a polished public page from Share Studio.';

  const extraContent = (() => {
    if (isMobile) {
      const menuItems: MenuProps['items'] = [
        {
          key: 'share',
          label: shareActionLabel,
          icon: shareState === 'public' ? <ShareAltOutlined /> : <EyeOutlined />,
          onClick: openShareSurface,
        },
        {
          key: 'studio',
          label: 'Share Studio',
          icon: <ShareAltOutlined />,
          onClick: openShareStudio,
        },
        {
          key: 'refresh',
          label: 'Refresh',
          icon: <SyncOutlined />,
          onClick: handleRefresh,
        },
      ];

      if (canManageCollaborators) {
        menuItems.unshift({
          key: 'collaborators',
          label: 'Collaborators',
          icon: <TeamOutlined />,
          onClick: () => setOpenCollaboratorModal(true),
        });
      }

      return (
        <Dropdown menu={{ items: menuItems }} placement="bottomRight">
          <Button icon={<MoreOutlined />} type="text" />
        </Dropdown>
      );
    }

    return (
      <Space wrap>
        <Button icon={<EyeOutlined />} onClick={openSharePreview}>
          {shareActionLabel}
        </Button>
        <Button icon={<ShareAltOutlined />} onClick={openShareStudio}>
          Share Studio
        </Button>
        {canManageCollaborators && (
          <Button icon={<TeamOutlined />} onClick={() => setOpenCollaboratorModal(true)}>
            Collaborators
          </Button>
        )}
        <Button icon={<SyncOutlined />} onClick={handleRefresh} loading={isRefreshing}>
          Refresh
        </Button>
      </Space>
    );
  })();

  return (
    <PageContainer
      onBack={() => history.push('/story')}
      title={queryDetailLoading ? 'Loading story...' : detail?.title || 'Story timeline'}
      extra={extraContent}
    >
      {detail && (
        <div
          style={{
            marginBottom: 20,
            padding: isMobile ? 20 : 24,
            borderRadius: 24,
            border: '1px solid rgba(24, 144, 255, 0.12)',
            background: 'linear-gradient(140deg, rgba(24,144,255,0.10), rgba(19,194,194,0.08))',
          }}
        >
          <div style={{ display: 'flex', justifyContent: 'space-between', gap: 16, flexWrap: 'wrap' }}>
            <div style={{ flex: '1 1 320px', minWidth: 0 }}>
              <Tag bordered={false} color="processing">
                Story canvas
              </Tag>
              <h2 style={{ margin: '12px 0 8px', fontSize: isMobile ? 24 : 30 }}>
                {detail.title || 'Untitled story'}
              </h2>
              <p style={{ margin: 0, color: '#5f6b7f', lineHeight: 1.75 }}>{summaryDescription}</p>
              <div style={{ display: 'flex', gap: 16, flexWrap: 'wrap', marginTop: 16, color: '#5f6b7f' }}>
                <span>{detail.storyTime || 'No story time set'}</span>
                <span>{Number(detail.itemCount || 0)} moments</span>
                <span>{detail.updateTime || 'No recent update'}</span>
                <span>{shareStatusText}</span>
              </div>
            </div>
            <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap', alignItems: 'flex-start' }}>
              <Button type="primary" icon={<ShareAltOutlined />} onClick={openShareStudio}>
                Open Studio
              </Button>
              <Button
                icon={shareState === 'public' ? <ShareAltOutlined /> : <EyeOutlined />}
                onClick={openShareSurface}
              >
                {shareActionLabel}
              </Button>
              {canEdit && (
                <Button
                  icon={<PlusOutlined />}
                  onClick={() => {
                    setCurrentOption('add');
                    setCurrentItem(undefined);
                    setOpenAddItemModal(true);
                  }}
                >
                  Add Moment
                </Button>
              )}
            </div>
          </div>
        </div>
      )}

      <div
        className="timeline"
        ref={containerRef}
        style={{
          height: 'calc(100vh - 200px)',
          overflow: 'auto',
          position: 'relative',
          padding: '0 8px',
        }}
      >
        {items.length > 0 ? (
          <PullToRefresh
            onRefresh={async () => {
              loadNewer();
            }}
            disabled={!isMobile}
          >
            {hasMoreNew && !isMobile && (
              <div style={{ textAlign: 'center', padding: '12px 0' }}>
                <Button onClick={loadNewer} loading={isRefreshing}>
                  Load Newer Moments
                </Button>
              </div>
            )}

            {Object.values(groupedItems)
              .sort((a, b) => b.sortValue - a.sortValue)
              .map(({ dateKey, items: dateItems, sortValue }) => (
                <div key={dateKey}>
                  <h2 className="timeline-section-header">{dateKey}</h2>
                  <SortableTimelineGrid
                    items={dateItems}
                    dateKey={dateKey}
                    sortValue={sortValue}
                    handleOption={(item, option) => {
                      setCurrentItem(item);
                      setCurrentOption(option);
                      setOpenAddItemModal(true);
                    }}
                    onOpenDetail={(item) => {
                      setDetailItem(item);
                      setOpenDetailDrawer(true);
                    }}
                    disableEdit={!canEdit}
                    refresh={refreshStory}
                    onOrderChange={(changedDateKey, newItems) => {
                      setItems((prev) => mergeGroupOrder(prev, changedDateKey, newItems));
                    }}
                  />
                </div>
              ))}

            {loading && <div className="load-indicator">Loading moments...</div>}
            {!loading && !hasMoreOld && <div className="no-more-data">All historical moments are loaded.</div>}

            {showScrollTop && (
              <div
                style={{
                  position: 'fixed',
                  right: 24,
                  bottom: 88,
                  zIndex: 10,
                }}
              >
                <Button type="primary" shape="circle" icon={<VerticalAlignTopOutlined />} onClick={scrollToTop} />
              </div>
            )}
          </PullToRefresh>
        ) : (
          <div
            style={{
              display: 'flex',
              flexDirection: 'column',
              justifyContent: 'center',
              alignItems: 'center',
              height: '100%',
              textAlign: 'center',
            }}
          >
            {loading ? (
              <>
                <Spin size="large" />
                <div style={{ marginTop: 16, fontSize: 16, color: '#666' }}>Loading timeline data...</div>
              </>
            ) : (
              <>
                <Empty
                  description="No moments yet"
                  image={Empty.PRESENTED_IMAGE_SIMPLE}
                  imageStyle={{ height: 60 }}
                >
                  <div style={{ fontSize: 16, color: '#999', marginBottom: 16 }}>
                    Start with a first memory, then curate it in Share Studio.
                  </div>
                </Empty>
                <Button
                  type="primary"
                  size="large"
                  disabled={!canEdit}
                  onClick={() => {
                    setCurrentOption('add');
                    setCurrentItem(undefined);
                    setOpenAddItemModal(true);
                  }}
                >
                  Add First Moment
                </Button>
              </>
            )}
          </div>
        )}
      </div>

      <FloatButton
        onClick={() => {
          setCurrentOption('add');
          setCurrentItem(undefined);
          setOpenAddItemModal(true);
        }}
        icon={<PlusOutlined />}
        disabled={!canEdit}
        type="primary"
        style={{ right: 24, bottom: 24 }}
      />

      <AddTimeLineItemModal
        visible={openAddItemModal}
        initialValues={currentItem}
        option={currentOption || 'add'}
        onCancel={() => setOpenAddItemModal(false)}
        onOk={() => {
          setOpenAddItemModal(false);
          refreshStory();
        }}
        storyId={lineId}
      />

      {detailItem && (
        <TimelineItemDrawer
          storyItem={detailItem}
          open={openDetailDrawer}
          setOpen={setOpenDetailDrawer}
          handleDelete={async () => {
            try {
              if (!detailItem.instanceId) return;

              const removeResponse = await removeStoryItem(detailItem.instanceId);
              if (removeResponse.code === 200) {
                message.success('Moment deleted.');
                setOpenDetailDrawer(false);
                refreshStory();
                return;
              }

              message.error('Delete failed.');
            } catch (error) {
              message.error('Delete failed.');
            }
          }}
          disableEdit={!canEdit}
          handOption={(item, option) => {
            setCurrentItem(item);
            setCurrentOption(option);
            setOpenDetailDrawer(false);
            setOpenAddItemModal(true);
          }}
        />
      )}

      <CollaboratorModal
        visible={openCollaboratorModal}
        onCancel={() => setOpenCollaboratorModal(false)}
        storyId={lineId ?? ''}
      />
    </PageContainer>
  );
};

export default Index;
