import dayjs from 'dayjs';
import type { ConfigType } from 'dayjs';
import type { FlatResponseData } from '@sa/axios';
import { WEB_SERVICE_PREFIX } from '@/constants/service';
import { request } from '../request';
import {
  type ProjectExecutionResponse,
  type TaskWorklogResponse,
  normalizeProjectLocalDate,
  normalizeTaskWorklog
} from './project-shared';
import { type ServiceRequestResult, mapServiceResult, normalizeStringId, safeJsonRequestConfig } from './shared';

type PersonalItemRecord = Api.PersonalItem.PersonalItem;
type PersonalItemWorklogRecord = Api.Project.TaskWorklog;
type PersonalItemResult<T> = Promise<FlatResponseData<any, T>>;
type StringIdResponse = string | number;
type PersonalItemLocalDateValue = string | number[] | null;
type AttachmentItemResponse = Omit<Api.Project.AttachmentItem, 'fileId'> & {
  fileId?: StringIdResponse;
  id?: StringIdResponse;
};
type PersonalItemLifecycleActionResponse = Omit<Api.PersonalItem.PersonalItemLifecycleAction, 'needReason'> & {
  needReason?: boolean | number | string | null;
};
type PersonalItemResponse = Omit<
  Api.PersonalItem.PersonalItem,
  | 'id'
  | 'ownerId'
  | 'terminal'
  | 'allowEdit'
  | 'availableActions'
  | 'plannedStartDate'
  | 'plannedEndDate'
  | 'actualStartDate'
  | 'actualEndDate'
  | 'attachments'
  | 'totalSpentHours'
> & {
  id: StringIdResponse;
  ownerId: StringIdResponse;
  terminal?: boolean | number | string | null;
  allowEdit?: boolean | number | string | null;
  availableActions?: PersonalItemLifecycleActionResponse[] | null;
  plannedStartDate?: PersonalItemLocalDateValue;
  plannedEndDate?: PersonalItemLocalDateValue;
  actualStartDate?: PersonalItemLocalDateValue;
  actualEndDate?: PersonalItemLocalDateValue;
  attachments?: AttachmentItemResponse[] | null;
  progressRate?: number | null;
  totalSpentHours?: number | string | null;
};
type PersonalItemPageResponse = Omit<Api.PersonalItem.PersonalItemPageResult, 'total' | 'list'> & {
  total: number | string;
  list: PersonalItemResponse[];
};
type PersonalItemWorklogPageResponse = Api.Project.PageResult<TaskWorklogResponse>;
type PersonalItemExecutionOptionResponse = ProjectExecutionResponse & {
  projectName?: string | null;
};
type PersonalItemSaveRequest = {
  executionId?: string;
  taskTitle: string;
  type: string;
  progressRate?: number;
  plannedStartDate?: string;
  plannedEndDate?: string;
  taskDesc?: string;
  attachments?: Array<{
    id?: string;
    url: string;
    name: string;
    size?: number;
    contentType?: string;
  }>;
};
type PersonalItemWorklogSaveRequest = {
  startDate: string;
  endDate: string;
  durationHours: number;
  progressRate: number;
  workContent?: string;
  attachments?: Array<{
    id?: string;
    url: string;
    name: string;
    size?: number;
    contentType?: string;
  }>;
  difficulty: string;
};

const PERSONAL_ITEM_PREFIX = `${WEB_SERVICE_PREFIX}/project/personal-items`;

const CURRENT_USER_ID = 'current-user';
const CURRENT_USER_NAME = '当前用户';

const personalItems: PersonalItemRecord[] = createSeedItems();
const personalItemWorklogs: PersonalItemWorklogRecord[] = createSeedWorklogs();
const executionOptions: Api.PersonalItem.PersonalItemExecutionOption[] = createExecutionOptions();

function createSuccessResult<T>(data: T): PersonalItemResult<T> {
  return Promise.resolve({
    data,
    error: null,
    response: undefined
  } as unknown as FlatResponseData<any, T>);
}

function normalizePageTotal(total: number | string) {
  const value = Number(total);

  return Number.isFinite(value) ? Math.max(0, value) : 0;
}

function normalizeAttachments(list?: AttachmentItemResponse[] | null): Api.Project.AttachmentItem[] | null {
  if (!list) {
    return null;
  }

  return list.map(item => {
    const rawId = item.fileId ?? item.id;

    return {
      ...item,
      fileId: rawId === null || rawId === undefined ? '' : String(rawId)
    };
  });
}

function normalizeBooleanFlag(value: boolean | number | string | null | undefined) {
  if (typeof value === 'boolean') {
    return value;
  }

  if (typeof value === 'number') {
    return value === 1;
  }

  if (typeof value === 'string') {
    const normalized = value.trim().toLowerCase();

    if (!normalized || normalized === '0' || normalized === 'false' || normalized === 'n') {
      return false;
    }

    return true;
  }

  return false;
}

function normalizeLifecycleActions(
  actions?: PersonalItemLifecycleActionResponse[] | null
): Api.PersonalItem.PersonalItemLifecycleAction[] {
  return (actions ?? []).map(action => ({
    actionCode: action.actionCode,
    actionName: action.actionName ?? '',
    needReason: normalizeBooleanFlag(action.needReason)
  }));
}

function normalizePersonalItem(response: PersonalItemResponse): Api.PersonalItem.PersonalItem {
  return {
    id: normalizeStringId(response.id),
    taskTitle: response.taskTitle ?? '',
    type: response.type ?? '',
    ownerId: normalizeStringId(response.ownerId),
    statusCode: response.statusCode,
    terminal: normalizeBooleanFlag(response.terminal),
    allowEdit: normalizeBooleanFlag(response.allowEdit),
    availableActions: normalizeLifecycleActions(response.availableActions),
    progressRate:
      typeof response.progressRate === 'number' ? response.progressRate : Number(response.progressRate ?? 0),
    totalSpentHours: (() => {
      if (typeof response.totalSpentHours === 'number') {
        return response.totalSpentHours;
      }

      if (response.totalSpentHours === null || response.totalSpentHours === undefined) {
        return null;
      }

      return Number(response.totalSpentHours);
    })(),
    plannedStartDate: normalizeProjectLocalDate(response.plannedStartDate),
    plannedEndDate: normalizeProjectLocalDate(response.plannedEndDate),
    actualStartDate: normalizeProjectLocalDate(response.actualStartDate),
    actualEndDate: normalizeProjectLocalDate(response.actualEndDate),
    taskDesc: response.taskDesc ?? null,
    lastStatusReason: response.lastStatusReason ?? null,
    attachments: normalizeAttachments(response.attachments),
    creator: response.creator ?? '',
    createTime: response.createTime ?? '',
    updater: response.updater ?? '',
    updateTime: response.updateTime ?? '',
    deleted: Boolean(response.deleted),
    ownerName: response.ownerName ?? null,
    ownerNickname: response.ownerNickname ?? null,
    statusName: response.statusName ?? null
  };
}

function normalizePersonalItemExecutionOption(
  response: PersonalItemExecutionOptionResponse
): Api.PersonalItem.PersonalItemExecutionOption {
  return {
    executionId: normalizeStringId(response.id),
    executionName: response.executionName ?? '',
    projectId: normalizeStringId(response.projectId),
    projectName: response.projectName ?? null
  };
}

function toPersonalItemSaveRequest(data: Api.PersonalItem.SavePersonalItemParams): PersonalItemSaveRequest {
  return {
    executionId: data.executionId ?? undefined,
    taskTitle: data.taskTitle.trim(),
    type: data.type,
    progressRate: typeof data.progressRate === 'number' ? data.progressRate : undefined,
    plannedStartDate: data.plannedStartDate ?? undefined,
    plannedEndDate: data.plannedEndDate ?? undefined,
    taskDesc: data.taskDesc ?? undefined,
    attachments:
      data.attachments?.map(item => ({
        id: item.fileId || undefined,
        url: item.url,
        name: item.name,
        size: item.size,
        contentType: item.contentType
      })) ?? undefined
  };
}

function toPersonalItemWorklogSaveRequest(
  data: Api.PersonalItem.SavePersonalItemWorklogParams
): PersonalItemWorklogSaveRequest {
  return {
    startDate: data.startDate,
    endDate: data.endDate,
    durationHours: Number(data.durationHours.toFixed(1)),
    progressRate: Number(data.progressRate.toFixed(2)),
    workContent: data.workContent ?? undefined,
    attachments:
      data.attachments?.map(item => ({
        id: item.fileId || undefined,
        url: item.url,
        name: item.name,
        size: item.size,
        contentType: item.contentType
      })) ?? undefined,
    difficulty: data.difficulty
  };
}

function createPersonalItemPageQuery(params: Api.PersonalItem.PersonalItemSearchParams = {}) {
  const query = new URLSearchParams();

  query.append('pageNo', String(params.pageNo ?? 1));
  query.append('pageSize', String(params.pageSize ?? 10));

  if (params.keyword) {
    query.append('keyword', params.keyword);
  }

  if (params.ownerId) {
    query.append('ownerId', params.ownerId);
  }

  if (params.statusCode) {
    query.append('statusCode', params.statusCode);
  }

  params.updateTime?.forEach(item => {
    if (item) {
      query.append('updateTime', item);
    }
  });

  return query.toString();
}

function createIdsQuery(ids: string[]) {
  const query = new URLSearchParams();

  ids.forEach(id => {
    if (id) {
      query.append('ids', id);
    }
  });

  return query.toString();
}

function createBindExecutionQuery(payload: Api.PersonalItem.BindPersonalItemExecutionParams) {
  const query = new URLSearchParams();

  payload.ids.forEach(id => {
    if (id) {
      query.append('itemIds', id);
    }
  });
  query.append('executionId', payload.executionId);

  return query.toString();
}

function cloneAttachment(item: Api.Project.AttachmentItem): Api.Project.AttachmentItem {
  return { ...item };
}

function cloneItem(item: PersonalItemRecord): PersonalItemRecord {
  return {
    ...item,
    attachments: item.attachments?.map(cloneAttachment) ?? null
  };
}

function cloneWorklog(item: PersonalItemWorklogRecord): PersonalItemWorklogRecord {
  return {
    ...item,
    attachments: item.attachments?.map(cloneAttachment) ?? null
  };
}

function normalizeDateTime(value?: ConfigType | null) {
  const target = value ? dayjs(value) : dayjs();
  return target.isValid() ? target.format('YYYY-MM-DD HH:mm:ss') : dayjs().format('YYYY-MM-DD HH:mm:ss');
}

function normalizeDate(value?: ConfigType | null) {
  if (!value) {
    return null;
  }

  const target = dayjs(value);
  return target.isValid() ? target.format('YYYY-MM-DD') : null;
}

function createSeedItems(): PersonalItemRecord[] {
  const now = dayjs();

  return [
    {
      id: 'personal-item-1',
      taskTitle: '整理供应商沟通纪要',
      type: 'daily',
      ownerId: CURRENT_USER_ID,
      statusCode: 'active',
      progressRate: 45,
      plannedStartDate: normalizeDate(now.subtract(3, 'day')),
      plannedEndDate: normalizeDate(now.add(2, 'day')),
      actualStartDate: normalizeDate(now.subtract(2, 'day')),
      actualEndDate: null,
      taskDesc: '<p>补齐今天会议纪要，沉淀成一页内部记录，便于后续同步。</p>',
      lastStatusReason: null,
      attachments: null,
      creator: CURRENT_USER_NAME,
      createTime: normalizeDateTime(now.subtract(3, 'day').hour(9).minute(20).second(0)),
      updater: CURRENT_USER_NAME,
      updateTime: normalizeDateTime(now.subtract(2, 'hour')),
      deleted: false,
      ownerName: CURRENT_USER_NAME,
      statusName: '进行中'
    },
    {
      id: 'personal-item-2',
      taskTitle: '清理浏览器收藏夹里的项目入口',
      type: 'daily',
      ownerId: CURRENT_USER_ID,
      statusCode: 'pending',
      progressRate: 0,
      plannedStartDate: normalizeDate(now.add(1, 'day')),
      plannedEndDate: normalizeDate(now.add(4, 'day')),
      actualStartDate: null,
      actualEndDate: null,
      taskDesc: '<p>把已经废弃的测试环境、旧文档入口统一清理。</p>',
      lastStatusReason: null,
      attachments: null,
      creator: CURRENT_USER_NAME,
      createTime: normalizeDateTime(now.subtract(2, 'day').hour(14).minute(10).second(0)),
      updater: CURRENT_USER_NAME,
      updateTime: normalizeDateTime(now.subtract(5, 'hour')),
      deleted: false,
      ownerName: CURRENT_USER_NAME,
      statusName: '待处理'
    },
    {
      id: 'personal-item-3',
      taskTitle: '补充账号开通说明截图',
      type: 'support',
      ownerId: CURRENT_USER_ID,
      statusCode: 'completed',
      progressRate: 100,
      plannedStartDate: normalizeDate(now.subtract(5, 'day')),
      plannedEndDate: normalizeDate(now.subtract(2, 'day')),
      actualStartDate: normalizeDate(now.subtract(5, 'day')),
      actualEndDate: normalizeDate(now.subtract(1, 'day')),
      taskDesc: '<p>为新同事入职说明补一版截图，后续发在群公告。</p>',
      lastStatusReason: '已完成并同步团队',
      attachments: null,
      creator: CURRENT_USER_NAME,
      createTime: normalizeDateTime(now.subtract(5, 'day').hour(11).minute(0).second(0)),
      updater: CURRENT_USER_NAME,
      updateTime: normalizeDateTime(now.subtract(1, 'day').hour(18).minute(30).second(0)),
      deleted: false,
      ownerName: CURRENT_USER_NAME,
      statusName: '已完成'
    }
  ];
}

function createSeedWorklogs(): PersonalItemWorklogRecord[] {
  const now = dayjs();

  return [
    {
      id: 'worklog-1',
      taskId: 'personal-item-1',
      userId: CURRENT_USER_ID,
      userNickname: CURRENT_USER_NAME,
      startDate: normalizeDate(now.subtract(2, 'day'))!,
      endDate: normalizeDate(now.subtract(2, 'day'))!,
      durationHours: 2.5,
      progressRate: 30,
      difficulty: '2',
      workContent: '整理会议录音和重点结论，先输出初版纪要。',
      attachments: null,
      createTime: normalizeDateTime(now.subtract(2, 'day').hour(19)),
      updateTime: normalizeDateTime(now.subtract(2, 'day').hour(19))
    },
    {
      id: 'worklog-2',
      taskId: 'personal-item-1',
      userId: CURRENT_USER_ID,
      userNickname: CURRENT_USER_NAME,
      startDate: normalizeDate(now.subtract(1, 'day'))!,
      endDate: normalizeDate(now.subtract(1, 'day'))!,
      durationHours: 1.5,
      progressRate: 45,
      difficulty: '2',
      workContent: '补全供应商待确认项并整理后续跟进人。',
      attachments: null,
      createTime: normalizeDateTime(now.subtract(1, 'day').hour(18)),
      updateTime: normalizeDateTime(now.subtract(1, 'day').hour(18))
    },
    {
      id: 'worklog-3',
      taskId: 'personal-item-3',
      userId: CURRENT_USER_ID,
      userNickname: CURRENT_USER_NAME,
      startDate: normalizeDate(now.subtract(5, 'day'))!,
      endDate: normalizeDate(now.subtract(5, 'day'))!,
      durationHours: 1,
      progressRate: 60,
      difficulty: '1',
      workContent: '补拍账号开通流程截图。',
      attachments: null,
      createTime: normalizeDateTime(now.subtract(5, 'day').hour(15)),
      updateTime: normalizeDateTime(now.subtract(5, 'day').hour(15))
    },
    {
      id: 'worklog-4',
      taskId: 'personal-item-3',
      userId: CURRENT_USER_ID,
      userNickname: CURRENT_USER_NAME,
      startDate: normalizeDate(now.subtract(1, 'day'))!,
      endDate: normalizeDate(now.subtract(1, 'day'))!,
      durationHours: 0.5,
      progressRate: 100,
      difficulty: '1',
      workContent: '校对文案并发到群公告。',
      attachments: null,
      createTime: normalizeDateTime(now.subtract(1, 'day').hour(18).minute(20)),
      updateTime: normalizeDateTime(now.subtract(1, 'day').hour(18).minute(20))
    }
  ];
}

function createExecutionOptions(): Api.PersonalItem.PersonalItemExecutionOption[] {
  return [
    {
      executionId: 'execution-1001',
      executionName: '2026Q2 运营提效',
      projectId: 'project-1001',
      projectName: '运营中台优化'
    },
    {
      executionId: 'execution-1002',
      executionName: '2026Q2 用户支持专项',
      projectId: 'project-1002',
      projectName: '基础平台升级'
    },
    {
      executionId: 'execution-1003',
      executionName: '2026Q3 数据治理',
      projectId: 'project-1003',
      projectName: '数据资产规范化'
    }
  ];
}

function findItemIndex(id: string) {
  return personalItems.findIndex(item => item.id === id);
}

function getItemOrThrow(id: string) {
  const item = personalItems.find(current => current.id === id && !current.deleted);

  if (!item) {
    throw new Error(`personal item not found: ${id}`);
  }

  return item;
}

function sortItems(list: PersonalItemRecord[]) {
  return [...list].sort((left, right) => dayjs(right.updateTime).valueOf() - dayjs(left.updateTime).valueOf());
}

function sortWorklogs(list: PersonalItemWorklogRecord[]) {
  return [...list].sort((left, right) => {
    const endDiff = dayjs(right.endDate).valueOf() - dayjs(left.endDate).valueOf();
    if (endDiff !== 0) {
      return endDiff;
    }
    return dayjs(right.updateTime).valueOf() - dayjs(left.updateTime).valueOf();
  });
}

function getPersonalItemStatusName(statusCode: Api.PersonalItem.PersonalItemStatusCode) {
  const statusNameMap: Partial<Record<Api.PersonalItem.PersonalItemStatusCode, string>> = {
    pending: '待处理',
    active: '进行中',
    completed: '已完成'
  };

  return statusNameMap[statusCode] || statusCode;
}

function removeItemsByIds(ids: string[]) {
  const idSet = new Set(ids);

  for (let i = personalItems.length - 1; i >= 0; i -= 1) {
    if (idSet.has(personalItems[i].id)) {
      personalItems.splice(i, 1);
    }
  }

  for (let i = personalItemWorklogs.length - 1; i >= 0; i -= 1) {
    if (idSet.has(personalItemWorklogs[i].taskId)) {
      personalItemWorklogs.splice(i, 1);
    }
  }
}

function sumWorklogHours(logs: PersonalItemWorklogRecord[]) {
  return logs.reduce((sum, log) => sum + (log.durationHours ?? 0), 0);
}

function syncItemFromWorklogs(itemId: string) {
  const item = getItemOrThrow(itemId);
  const logs = sortWorklogs(personalItemWorklogs.filter(log => log.taskId === itemId));

  item.statusName = getPersonalItemStatusName(item.statusCode);
  item.totalSpentHours = sumWorklogHours(logs);

  if (logs.length === 0) {
    if (item.statusCode !== 'completed') {
      item.progressRate = 0;
      item.actualStartDate = null;
      item.actualEndDate = null;
    }
    return;
  }

  const latestLog = logs[0];
  const chronologicalLogs = [...logs].sort(
    (left, right) => dayjs(left.startDate).valueOf() - dayjs(right.startDate).valueOf()
  );

  item.progressRate = latestLog.progressRate ?? item.progressRate;
  item.actualStartDate = chronologicalLogs[0]?.startDate ?? item.actualStartDate;
  item.actualEndDate = latestLog.endDate ?? item.actualEndDate;
  item.updateTime = latestLog.updateTime;
  item.updater = CURRENT_USER_NAME;

  if (item.statusCode === 'pending') {
    item.statusCode = 'active';
    item.statusName = getPersonalItemStatusName(item.statusCode);
  }
}

function applySaveFields(target: PersonalItemRecord, payload: Api.PersonalItem.SavePersonalItemParams) {
  target.taskTitle = payload.taskTitle.trim();
  target.type = payload.type;
  target.ownerId = payload.ownerId || target.ownerId;
  target.ownerName = CURRENT_USER_NAME;
  target.plannedStartDate = payload.plannedStartDate;
  target.plannedEndDate = payload.plannedEndDate;
  target.taskDesc = payload.taskDesc ?? null;
  target.attachments = payload.attachments?.map(cloneAttachment) ?? null;
  target.updater = CURRENT_USER_NAME;
  target.updateTime = normalizeDateTime();
}

function filterWorklogs(taskId: string, params?: Api.PersonalItem.PersonalItemWorklogSearchParams) {
  return sortWorklogs(
    personalItemWorklogs.filter(item => {
      if (item.taskId !== taskId) {
        return false;
      }

      if (params?.userId && item.userId !== params.userId) {
        return false;
      }

      if (params?.startDate && dayjs(item.endDate).isBefore(dayjs(params.startDate), 'day')) {
        return false;
      }

      if (params?.endDate && dayjs(item.startDate).isAfter(dayjs(params.endDate), 'day')) {
        return false;
      }

      return true;
    })
  );
}

export async function fetchGetPersonalItemPage(params: Api.PersonalItem.PersonalItemSearchParams = {}) {
  const query = createPersonalItemPageQuery(params);

  const result = await request<PersonalItemPageResponse>({
    ...safeJsonRequestConfig,
    url: query ? `${PERSONAL_ITEM_PREFIX}/page?${query}` : `${PERSONAL_ITEM_PREFIX}/page`,
    method: 'get'
  });

  return mapServiceResult(result as ServiceRequestResult<PersonalItemPageResponse>, data => ({
    total: normalizePageTotal(data.total),
    list: data.list.map(normalizePersonalItem)
  }));
}

export async function fetchGetPersonalItemDetail(id: string) {
  const result = await request<PersonalItemResponse>({
    ...safeJsonRequestConfig,
    url: `${PERSONAL_ITEM_PREFIX}/${id}`,
    method: 'get'
  });

  return mapServiceResult(result as ServiceRequestResult<PersonalItemResponse>, normalizePersonalItem);
}

export async function fetchCreatePersonalItem(data: Api.PersonalItem.SavePersonalItemParams) {
  const result = await request<string | number>({
    ...safeJsonRequestConfig,
    url: PERSONAL_ITEM_PREFIX,
    method: 'post',
    data: toPersonalItemSaveRequest(data)
  });

  const mapped = mapServiceResult(result as ServiceRequestResult<string | number>, normalizeStringId);

  if (!mapped.error && mapped.data) {
    const now = normalizeDateTime();
    const createdItem: PersonalItemRecord = {
      id: mapped.data,
      taskTitle: data.taskTitle.trim(),
      type: data.type,
      ownerId: data.ownerId || CURRENT_USER_ID,
      statusCode: 'pending',
      progressRate: typeof data.progressRate === 'number' ? data.progressRate : 0,
      plannedStartDate: data.plannedStartDate,
      plannedEndDate: data.plannedEndDate,
      actualStartDate: null,
      actualEndDate: null,
      taskDesc: data.taskDesc ?? null,
      lastStatusReason: null,
      attachments: data.attachments?.map(cloneAttachment) ?? null,
      creator: CURRENT_USER_NAME,
      createTime: now,
      updater: CURRENT_USER_NAME,
      updateTime: now,
      deleted: false,
      ownerName: CURRENT_USER_NAME,
      statusName: getPersonalItemStatusName('pending')
    };

    personalItems.unshift(createdItem);
  }

  return mapped;
}

export async function fetchUpdatePersonalItem(data: Api.PersonalItem.UpdatePersonalItemParams) {
  const result = await request<boolean>({
    ...safeJsonRequestConfig,
    url: `${PERSONAL_ITEM_PREFIX}/${data.id}`,
    method: 'put',
    data: toPersonalItemSaveRequest(data)
  });

  const mapped = mapServiceResult(result as ServiceRequestResult<boolean>, value => Boolean(value));

  if (!mapped.error && mapped.data) {
    const targetIndex = findItemIndex(data.id);

    if (targetIndex >= 0) {
      applySaveFields(personalItems[targetIndex], data);
    }
  }

  return mapped;
}

export async function fetchChangePersonalItemStatus(id: string, data: Api.PersonalItem.ChangePersonalItemStatusParams) {
  const result = await request<boolean>({
    ...safeJsonRequestConfig,
    url: `${PERSONAL_ITEM_PREFIX}/${id}/change-status`,
    method: 'post',
    data: {
      actionCode: data.actionCode,
      reason: data.reason ?? undefined
    }
  });

  const mapped = mapServiceResult(result as ServiceRequestResult<boolean>, value => Boolean(value));

  if (!mapped.error && mapped.data) {
    const target = personalItems.find(item => item.id === id);

    if (target) {
      target.lastStatusReason = data.reason ?? null;
      target.updater = CURRENT_USER_NAME;
      target.updateTime = normalizeDateTime();

      if (data.actionCode === 'start') {
        target.statusCode = 'active';
        target.statusName = getPersonalItemStatusName('active');
        target.actualStartDate ??= normalizeDate(dayjs());
        target.actualEndDate = null;
      } else if (data.actionCode === 'complete') {
        target.statusCode = 'completed';
        target.statusName = getPersonalItemStatusName('completed');
        target.progressRate = 100;
        target.actualStartDate ??= normalizeDate(dayjs());
        target.actualEndDate = normalizeDate(dayjs());
      } else if (data.actionCode === 'reopen') {
        target.statusCode = 'active';
        target.statusName = getPersonalItemStatusName('active');
        target.actualStartDate ??= normalizeDate(dayjs());
        target.actualEndDate = null;
      }
    }
  }

  return mapped;
}

export async function fetchDeletePersonalItem(id: string) {
  const result = await request<boolean>({
    ...safeJsonRequestConfig,
    url: `${PERSONAL_ITEM_PREFIX}/delete`,
    method: 'delete',
    params: { id }
  });

  const mapped = mapServiceResult(result as ServiceRequestResult<boolean>, value => Boolean(value));

  if (!mapped.error && mapped.data) {
    removeItemsByIds([id]);
  }

  return mapped;
}

export async function fetchBatchDeletePersonalItems(payload: Api.PersonalItem.BatchDeletePersonalItemParams) {
  const query = createIdsQuery(payload.ids);
  const result = await request<boolean>({
    ...safeJsonRequestConfig,
    url: query ? `${PERSONAL_ITEM_PREFIX}/delete-list?${query}` : `${PERSONAL_ITEM_PREFIX}/delete-list`,
    method: 'delete'
  });

  const mapped = mapServiceResult(result as ServiceRequestResult<boolean>, value => Boolean(value));

  if (!mapped.error && mapped.data) {
    removeItemsByIds(payload.ids);
  }

  return mapped;
}

export async function fetchGetPersonalItemExecutionOptions() {
  const result = await request<PersonalItemExecutionOptionResponse[]>({
    ...safeJsonRequestConfig,
    url: `${PERSONAL_ITEM_PREFIX}/owner/all-execution`,
    method: 'get'
  });

  return mapServiceResult(result as ServiceRequestResult<PersonalItemExecutionOptionResponse[]>, data =>
    data.map(normalizePersonalItemExecutionOption)
  );
}

export async function fetchBindPersonalItemsToExecution(payload: Api.PersonalItem.BindPersonalItemExecutionParams) {
  const query = createBindExecutionQuery(payload);
  const result = await request<boolean>({
    ...safeJsonRequestConfig,
    url: query ? `${PERSONAL_ITEM_PREFIX}/relate-execution?${query}` : `${PERSONAL_ITEM_PREFIX}/relate-execution`,
    method: 'post'
  });

  return mapServiceResult(result as ServiceRequestResult<boolean>, value => Boolean(value));
}

export function fetchStartPersonalItem(id: string): PersonalItemResult<boolean> {
  return fetchChangePersonalItemStatus(id, { actionCode: 'start' }) as PersonalItemResult<boolean>;
}

export function fetchCompletePersonalItem(id: string): PersonalItemResult<boolean> {
  return fetchChangePersonalItemStatus(id, { actionCode: 'complete' }) as PersonalItemResult<boolean>;
}

export function fetchReopenPersonalItem(id: string): PersonalItemResult<boolean> {
  return fetchChangePersonalItemStatus(id, { actionCode: 'reopen' }) as PersonalItemResult<boolean>;
}

export async function fetchGetPersonalItemWorklogPage(
  taskId: string,
  params: Api.PersonalItem.PersonalItemWorklogSearchParams = {}
) {
  const result = await request<PersonalItemWorklogPageResponse>({
    ...safeJsonRequestConfig,
    url: `${PERSONAL_ITEM_PREFIX}/${taskId}/worklogs`,
    method: 'get',
    params
  });

  return mapServiceResult(result as ServiceRequestResult<PersonalItemWorklogPageResponse>, data => ({
    ...data,
    list: data.list.map(normalizeTaskWorklog)
  }));
}

export async function fetchCreatePersonalItemWorklog(
  taskId: string,
  data: Api.PersonalItem.SavePersonalItemWorklogParams
) {
  const result = await request<string | number>({
    ...safeJsonRequestConfig,
    url: `${PERSONAL_ITEM_PREFIX}/${taskId}/worklogs`,
    method: 'post',
    data: toPersonalItemWorklogSaveRequest(data)
  });

  return mapServiceResult(result as ServiceRequestResult<string | number>, normalizeStringId);
}

export function fetchUpdatePersonalItemWorklog(
  taskId: string,
  payload: { worklogId: string; data: Api.PersonalItem.SavePersonalItemWorklogParams }
): PersonalItemResult<boolean> {
  return request<boolean>({
    ...safeJsonRequestConfig,
    url: `${PERSONAL_ITEM_PREFIX}/${taskId}/worklogs/${payload.worklogId}`,
    method: 'put',
    data: toPersonalItemWorklogSaveRequest(payload.data)
  });
}

export function fetchDeletePersonalItemWorklog(taskId: string, worklogId: string): PersonalItemResult<boolean> {
  return request<boolean>({
    ...safeJsonRequestConfig,
    url: `${PERSONAL_ITEM_PREFIX}/${taskId}/worklogs/${worklogId}`,
    method: 'delete'
  });
}
