import { Board } from '@ablogcms/kanban';
import type { BoardData, KanbanComponents } from '@ablogcms/kanban';
import { Fragment, useCallback, useRef, useState } from 'react';
import { random } from '@ablogcms/utils';
import { dialog } from '@ablogcms/dialog';
import AdminNewCard from '../admin-new-card/admin-new-card';
import AdminCard from '../admin-card/admin-card';
import AdminLaneHeader from '../admin-lane-header/admin-lane-header';
import type { AdminCardLaneType, AdminCardType, AdminBoardDataType } from '../../types';

interface AdminMenuProps {
  data: AdminBoardDataType;
}

function AdminMenuEditor({ data: initialData }: AdminMenuProps) {
  const [data, setData] = useState(initialData);
  // 初回ロード（onDataChange 経由での初期同期）と、ユーザーによる最初の実編集を区別するためのフラグ。
  // 再レンダーを伴わないため state ではなく ref で保持する。
  const didMountRef = useRef(false);
  const firstEditRef = useRef(true);

  const onDataChange = useCallback((nextData: AdminBoardDataType) => {
    if (!didMountRef.current) {
      didMountRef.current = true;
      setData(nextData);
      return;
    }
    if (firstEditRef.current) {
      firstEditRef.current = false;
      $('.js-config-not-saved').addClass('active');
    }
    setData(nextData);
  }, []);

  const doneEdit = useCallback((newCard: AdminCardType) => {
    setData((prev) => ({
      ...prev,
      lanes: prev.lanes.map((lane) =>
        lane.id === newCard.laneId
          ? { ...lane, cards: lane.cards.map((card) => (card.id === newCard.id ? newCard : card)) }
          : lane
      ),
    }));
  }, []);

  const doneLaneEdit = useCallback((newLane: AdminCardLaneType) => {
    setData((prev) => ({
      ...prev,
      lanes: prev.lanes.map((lane) => (lane.id === newLane.id ? newLane : lane)),
    }));
  }, []);

  const addLane = useCallback(() => {
    setData((prev) => {
      const { lanes } = prev;
      return {
        lanes: [
          lanes[0],
          {
            id: random(),
            movable: true,
            title: '新規',
            index: 1,
            cards: [],
          },
          ...lanes.slice(1),
        ],
      };
    });
  }, []);

  const removeLane = useCallback(
    async (id: string) => {
      const lane = data.lanes.find((l) => l.id === id);
      if (!lane) {
        return;
      }
      if (lane.cards.length) {
        await dialog.alert(ACMS.i18n('admin_menu.lane_alert'));
        return;
      }
      if (await dialog.confirm(ACMS.i18n('admin_menu.lane_remove_confirm'))) {
        setData((prev) => ({
          lanes: prev.lanes.filter((l) => l.id !== id),
        }));
      }
    },
    [data.lanes]
  );

  // @ablogcms/kanban の LaneData/CardData は string 索引シグネチャを持つ汎用形。
  // 管理メニュー固有の AdminCardLaneType/AdminCardType は構造的に互換だが同シグネチャを持たないため境界でキャストする。
  const renderLaneHeader: KanbanComponents['LaneHeader'] = useCallback(
    ({ lane }) => (
      <AdminLaneHeader
        lane={lane as unknown as AdminCardLaneType}
        doneEdit={doneLaneEdit}
        addLane={addLane}
        removeLane={removeLane}
      />
    ),
    [doneLaneEdit, addLane, removeLane]
  );

  const renderCard: KanbanComponents['Card'] = useCallback(
    ({ card, onDelete }) => (
      <AdminCard card={card as unknown as AdminCardType} onDelete={onDelete} doneEdit={doneEdit} />
    ),
    [doneEdit]
  );

  const components: Partial<KanbanComponents> = {
    NewCard: AdminNewCard,
    LaneHeader: renderLaneHeader,
    Card: renderCard,
  };

  return (
    <>
      <Board
        // @ablogcms/kanban の BoardData/CardData は string 索引シグネチャを持つ汎用形。
        // 管理メニュー固有の AdminBoardDataType は構造的に互換だが同シグネチャを持たないため境界でキャストする。
        data={data as unknown as BoardData}
        draggable
        editable
        style={{ border: '1px solid #F1F1F1', backgroundColor: '#FFF' }}
        canAddLanes
        addLaneTitle={ACMS.i18n('admin_menu.add_lane')}
        addCardTitle={ACMS.i18n('admin_menu.add_card')}
        hideCardDeleteIcon
        customCardLayout
        components={components}
        handleLaneDragStart={() => false}
        onDataChange={(currentData) => {
          // 上記キャストの逆方向。@ablogcms/kanban 側の型に索引シグネチャが無いため直接代入できない。
          onDataChange(currentData as unknown as AdminBoardDataType);
        }}
      />
      {data.lanes.map((lane) => (
        <Fragment key={lane.id}>
          <input type="hidden" name="admin_menu_lane_title[]" value={lane.title} />
          <input type="hidden" name="admin_menu_lane_id[]" value={lane.id} />
          {lane.cards.map((card) => (
            <Fragment key={card.id}>
              <input type="hidden" name="admin_menu_card_title[]" value={card.title} />
              <input type="hidden" name="admin_menu_card_url[]" value={card.url} />
              <input type="hidden" name="admin_menu_card_laneid[]" value={lane.id} />
              <input type="hidden" name="admin_menu_card_id[]" value={card.id} />
              <input type="hidden" name="admin_menu_card_icon[]" value={card.icon} />
              <input type="hidden" name="admin_menu_card_admin[]" value={`${card.admin}`} />
            </Fragment>
          ))}
        </Fragment>
      ))}
      <input type="hidden" name="@admin_menu_lane_group[]" value="admin_menu_lane_title" />
      <input type="hidden" name="config[]" value="admin_menu_lane_title" />
      <input type="hidden" name="@admin_menu_lane_group[]" value="admin_menu_lane_id" />
      <input type="hidden" name="config[]" value="admin_menu_lane_id" />
      <input type="hidden" name="@admin_menu_card_group[]" value="admin_menu_card_title" />
      <input type="hidden" name="config[]" value="admin_menu_card_title" />
      <input type="hidden" name="@admin_menu_card_group[]" value="admin_menu_card_url" />
      <input type="hidden" name="config[]" value="admin_menu_card_url" />
      <input type="hidden" name="@admin_menu_card_group[]" value="admin_menu_card_laneid" />
      <input type="hidden" name="config[]" value="admin_menu_card_laneid" />
      <input type="hidden" name="@admin_menu_card_group[]" value="admin_menu_card_id" />
      <input type="hidden" name="config[]" value="admin_menu_card_id" />
      <input type="hidden" name="@admin_menu_card_group[]" value="admin_menu_card_icon" />
      <input type="hidden" name="config[]" value="admin_menu_card_icon" />
      <input type="hidden" name="@admin_menu_card_group[]" value="admin_menu_card_admin" />
      <input type="hidden" name="config[]" value="admin_menu_card_admin" />
      <input type="hidden" name="config[]" value="@admin_menu_lane_group" />
      <input type="hidden" name="config[]" value="@admin_menu_card_group" />
    </>
  );
}

export default AdminMenuEditor;
