import dayjs from 'dayjs';
import { normalizeNullableStringId, normalizeStringId } from './shared';

type ProjectStatusCode = Api.Project.ProjectStatusCode;
type ProjectStatusActionCode = Exclude<Api.Project.ProjectStatusActionCode, 'auto_start'>;

type StringIdResponse = string | number;

export type ProjectLocalDateValue = string | number[] | null;

export type LifecycleActionResponse<ActionCode extends string> = Partial<Api.Project.LifecycleAction<ActionCode>> & {
  actionCode: ActionCode;
};

export type ProjectExecutionResponse = Omit<
  Api.Project.ProjectExecution,
  | 'id'
  | 'projectId'
  | 'projectRequirementId'
  | 'ownerId'
  | 'availableActions'
  | 'plannedStartDate'
  | 'plannedEndDate'
  | 'actualStartDate'
  | 'actualEndDate'
  | 'progressRate'
  | 'priority'
  | 'priorityName'
> & {
  id: StringIdResponse;
  projectId: StringIdResponse;
  projectRequirementId?: StringIdResponse | null;
  ownerId: StringIdResponse;
  availableActions?: LifecycleActionResponse<Api.Project.ProjectExecutionActionCode>[] | null;
  plannedStartDate?: ProjectLocalDateValue;
  plannedEndDate?: ProjectLocalDateValue;
  actualStartDate?: ProjectLocalDateValue;
  actualEndDate?: ProjectLocalDateValue;
  progressRate?: number | null;
  priority?: string | number | null;
  priorityName?: string | null;
};

export type MyExecutionResponse = Omit<
  Api.Project.MyExecutionItem,
  | 'id'
  | 'projectId'
  | 'projectRequirementId'
  | 'priority'
  | 'progressRate'
  | 'plannedStartDate'
  | 'plannedEndDate'
  | 'actualStartDate'
  | 'actualEndDate'
> & {
  id: StringIdResponse;
  projectId: StringIdResponse;
  projectRequirementId?: StringIdResponse | null;
  priority?: string | number | null;
  progressRate?: number | null;
  plannedStartDate?: ProjectLocalDateValue;
  plannedEndDate?: ProjectLocalDateValue;
  actualStartDate?: ProjectLocalDateValue;
  actualEndDate?: ProjectLocalDateValue;
};

export type MyParticipatedProjectResponse = Omit<Api.Project.MyParticipatedProjectItem, 'id'> & {
  id: StringIdResponse;
};

export type MyOwnedProjectMemberResponse = Omit<Api.Project.MyOwnedProjectMember, 'userId'> & {
  userId: StringIdResponse;
};

export type MyOwnedProjectResponse = Omit<Api.Project.MyOwnedProjectItem, 'id' | 'members'> & {
  id: StringIdResponse;
  members?: MyOwnedProjectMemberResponse[] | null;
};

export type MyTaskResponse = Omit<
  Api.Project.MyTaskItem,
  | 'id'
  | 'projectId'
  | 'executionId'
  | 'priority'
  | 'plannedEndDate'
  | 'progressRate'
  | 'createTime'
  | 'parentTaskId'
  | 'availableActions'
> & {
  id: StringIdResponse;
  projectId: StringIdResponse;
  executionId?: StringIdResponse | null;
  priority?: string | number | null;
  plannedEndDate?: ProjectLocalDateValue;
  progressRate?: number | string | null;
  createTime?: string | number | null;
  parentTaskId?: StringIdResponse | null;
  availableActions?: LifecycleActionResponse<Api.Project.ProjectTaskActionCode>[] | null;
};

export type TeamLoadDistributionItemResponse = Omit<Api.Project.TeamLoadDistributionItem, 'projectId'> & {
  projectId?: StringIdResponse | null;
};

export type TeamLoadMemberResponse = Omit<Api.Project.TeamLoadMember, 'userId' | 'items'> & {
  userId: StringIdResponse;
  items?: TeamLoadDistributionItemResponse[] | null;
};

export type TeamLoadResponse = {
  members?: TeamLoadMemberResponse[] | null;
};

export type WorklogDistributionItemResponse = Omit<Api.Project.WorklogDistributionItem, 'projectId'> & {
  projectId?: StringIdResponse | null;
};

export type MyWorklogWeekResponse = Omit<Api.Project.MyWorklogWeekResult, 'dailyHours' | 'distribution'> & {
  dailyHours?: number[] | null;
  distribution?: WorklogDistributionItemResponse[] | null;
};

export type TeamWorklogWeekMemberResponse = Omit<Api.Project.TeamWorklogWeekMember, 'userId' | 'items'> & {
  userId: StringIdResponse;
  items?: WorklogDistributionItemResponse[] | null;
};

export type TeamWorklogWeekResponse = Omit<Api.Project.TeamWorklogWeekResult, 'members'> & {
  members?: TeamWorklogWeekMemberResponse[] | null;
};

export type ExecutionAssigneeResponse = Omit<Api.Project.ExecutionAssignee, 'id' | 'executionId' | 'userId'> & {
  id: StringIdResponse;
  executionId: StringIdResponse;
  userId: StringIdResponse;
};

export type ExecutionAssigneeLogResponse = Omit<
  Api.Project.ExecutionAssigneeLog,
  'id' | 'executionId' | 'userId' | 'operatorUserId'
> & {
  id: StringIdResponse;
  executionId: StringIdResponse;
  userId: StringIdResponse;
  operatorUserId: StringIdResponse;
};

type TaskAssigneeRefResponse = Omit<Api.Project.TaskAssigneeRef, 'id' | 'userId'> & {
  id: StringIdResponse;
  userId: StringIdResponse;
};

/**
 * 后端 attachments 项的兼容形态：历史/当前响应字段名是 `id`，前端类型统一用 `fileId`。
 * normalizeAttachments 负责把两者归一成 `fileId`。
 */
type AttachmentItemResponse = Omit<Api.Project.AttachmentItem, 'fileId'> & {
  fileId?: StringIdResponse;
  id?: StringIdResponse;
};

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)
    };
  });
}

/**
 * 5.6 单独接口返的协办人字段（与 5.3 嵌入字段命名口径不一致：返 userNickname 而非 nickname）。
 * 经 normalizeTaskAssignee 归一化后对外统一为 Api.Project.TaskAssigneeRef。
 */
export type TaskAssigneeFromApiResponse = {
  id: StringIdResponse;
  taskId: StringIdResponse;
  userId: StringIdResponse;
  userNickname?: string | null;
  joinedAt?: string | null;
};

export type TaskAssigneeLogResponse = Omit<
  Api.Project.TaskAssigneeLog,
  'id' | 'taskId' | 'userId' | 'operatorUserId'
> & {
  id: StringIdResponse;
  taskId: StringIdResponse;
  userId: StringIdResponse;
  operatorUserId: StringIdResponse;
};

export type ProjectTaskResponse = Omit<
  Api.Project.ProjectTask,
  | 'id'
  | 'projectId'
  | 'executionId'
  | 'parentTaskId'
  | 'ownerId'
  | 'executionOwnerId'
  | 'parentTaskOwnerId'
  | 'availableActions'
  | 'plannedStartDate'
  | 'plannedEndDate'
  | 'actualStartDate'
  | 'actualEndDate'
  | 'progressRate'
  | 'assignees'
  | 'attachments'
  | 'priority'
  | 'priorityName'
> & {
  id: StringIdResponse;
  projectId: StringIdResponse;
  executionId: StringIdResponse;
  executionName?: string | null;
  executionStatusCode?: Api.Project.ProjectExecutionStatusCode | null;
  parentTaskId?: StringIdResponse | null;
  ownerId: StringIdResponse;
  executionOwnerId?: StringIdResponse | null;
  parentTaskOwnerId?: StringIdResponse | null;
  availableActions?: LifecycleActionResponse<Api.Project.ProjectTaskActionCode>[] | null;
  plannedStartDate?: ProjectLocalDateValue;
  plannedEndDate?: ProjectLocalDateValue;
  actualStartDate?: ProjectLocalDateValue;
  actualEndDate?: ProjectLocalDateValue;
  progressRate?: number | null;
  assignees?: TaskAssigneeRefResponse[] | null;
  attachments?: AttachmentItemResponse[] | null;
  totalSpentHours?: number | null;
  priority?: string | number | null;
  priorityName?: string | null;
};

export type TaskWorklogResponse = Omit<
  Api.Project.TaskWorklog,
  'id' | 'taskId' | 'userId' | 'difficulty' | 'attachments' | 'startDate' | 'endDate'
> & {
  id: StringIdResponse;
  taskId: StringIdResponse;
  userId: StringIdResponse;
  difficulty?: string | null;
  attachments?: AttachmentItemResponse[] | null;
  startDate?: ProjectLocalDateValue;
  endDate?: ProjectLocalDateValue;
};

export interface ProjectMemberResponse {
  id: string | number;
  userId: string | number;
  userNickname: string;
  roleId: string | number;
  roleName: string;
  roleCode: string;
  managerFlag: boolean;
  status: 0 | 1;
  joinedTime: string;
  leftTime?: string | null;
  remark?: string | null;
}

const projectLifecycleActionNameMap: Record<ProjectStatusActionCode, string> = {
  pause: '暂停项目',
  resume: '恢复项目',
  complete: '完成项目',
  cancel: '取消项目',
  reopen: '重新开启',
  archive: '归档项目'
};

const projectLifecycleActionReasonRequiredMap: Record<ProjectStatusActionCode, boolean> = {
  pause: true,
  resume: false,
  complete: true,
  cancel: true,
  reopen: true,
  archive: false
};

const projectLifecycleActionMap: Record<ProjectStatusCode, ProjectStatusActionCode[]> = {
  pending: ['cancel'],
  active: ['pause', 'complete', 'cancel'],
  paused: ['resume', 'cancel'],
  completed: ['reopen', 'archive'],
  cancelled: [],
  archived: []
};

export function getProjectLifecycleActions(statusCode: ProjectStatusCode): Api.Project.ProjectLifecycleAction[] {
  return projectLifecycleActionMap[statusCode].map(actionCode => ({
    actionCode,
    actionName: projectLifecycleActionNameMap[actionCode],
    needReason: projectLifecycleActionReasonRequiredMap[actionCode]
  }));
}

export function normalizeProjectLocalDate(value: ProjectLocalDateValue | undefined) {
  if (value === null || value === undefined || value === '') {
    return null;
  }

  if (Array.isArray(value)) {
    const [year, month, day] = value;

    if (!year || !month || !day) {
      return null;
    }

    return [year, month, day].map(item => String(item).padStart(2, '0')).join('-');
  }

  return String(value);
}

/**
 * 后端 LocalDateTime 统一序列化为毫秒时间戳（也可能是数字字符串/格式化字符串），
 * 归一为 'YYYY-MM-DD HH:mm:ss' 供展示与 dayjs 解析。
 */
export function normalizeProjectDateTime(value: string | number | null | undefined): string {
  if (value === null || value === undefined || value === '') {
    return '';
  }

  let parsed: dayjs.Dayjs;
  if (typeof value === 'number') {
    parsed = dayjs(value);
  } else if (/^\d+$/.test(value)) {
    // 字符串形态的毫秒时间戳：dayjs 无法直接解析，先转数值（时间值非 ID，安全整数范围内）
    parsed = dayjs(Number(value));
  } else {
    parsed = dayjs(value);
  }

  return parsed.isValid() ? parsed.format('YYYY-MM-DD HH:mm:ss') : '';
}

export function normalizeLifecycleActions<ActionCode extends string>(
  actions: LifecycleActionResponse<ActionCode>[] | null | undefined
): Api.Project.LifecycleAction<ActionCode>[] {
  return (actions ?? []).map(action => ({
    actionCode: action.actionCode,
    actionName: action.actionName ?? '',
    needReason: Boolean(action.needReason)
  }));
}

export function normalizeProjectMember(response: ProjectMemberResponse): Api.Project.ProjectMember {
  return {
    id: normalizeStringId(response.id),
    userId: normalizeStringId(response.userId),
    userNickname: response.userNickname || '',
    roleId: normalizeStringId(response.roleId),
    roleName: response.roleName || '',
    roleCode: response.roleCode || '',
    managerFlag: Boolean(response.managerFlag),
    status: response.status,
    joinedTime: response.joinedTime,
    leftTime: response.leftTime ?? null,
    remark: response.remark ?? null
  };
}

function normalizePriority(value: string | number | null | undefined): string {
  if (value === null || value === undefined || value === '') {
    return '1';
  }
  return String(value);
}

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

  const numeric = typeof value === 'number' ? value : Number(value ?? 0);
  return Number.isFinite(numeric) ? numeric : null;
}

export function normalizeProjectExecution(response: ProjectExecutionResponse): Api.Project.ProjectExecution {
  return {
    ...response,
    id: normalizeStringId(response.id),
    projectId: normalizeStringId(response.projectId),
    projectRequirementId: normalizeNullableStringId(response.projectRequirementId),
    projectRequirementName: response.projectRequirementName ?? null,
    projectRequirementStatusCode: response.projectRequirementStatusCode ?? null,
    ownerId: normalizeStringId(response.ownerId),
    ownerNickname: response.ownerNickname ?? null,
    statusName: response.statusName ?? null,
    terminal: Boolean(response.terminal),
    allowEdit: Boolean(response.allowEdit),
    availableActions: normalizeLifecycleActions(response.availableActions),
    plannedStartDate: normalizeProjectLocalDate(response.plannedStartDate),
    plannedEndDate: normalizeProjectLocalDate(response.plannedEndDate),
    actualStartDate: normalizeProjectLocalDate(response.actualStartDate),
    actualEndDate: normalizeProjectLocalDate(response.actualEndDate),
    progressRate: typeof response.progressRate === 'number' ? response.progressRate : 0,
    priority: normalizePriority(response.priority),
    priorityName: response.priorityName ?? null,
    executionDesc: response.executionDesc ?? null,
    lastStatusReason: response.lastStatusReason ?? null
  };
}

export function normalizeMyExecution(response: MyExecutionResponse): Api.Project.MyExecutionItem {
  return {
    ...response,
    id: normalizeStringId(response.id),
    projectId: normalizeStringId(response.projectId),
    statusName: response.statusName ?? null,
    priority: normalizePriority(response.priority),
    progressRate: typeof response.progressRate === 'number' ? response.progressRate : 0,
    plannedStartDate: normalizeProjectLocalDate(response.plannedStartDate),
    plannedEndDate: normalizeProjectLocalDate(response.plannedEndDate),
    actualStartDate: normalizeProjectLocalDate(response.actualStartDate),
    actualEndDate: normalizeProjectLocalDate(response.actualEndDate),
    projectRequirementId: normalizeNullableStringId(response.projectRequirementId),
    projectRequirementName: response.projectRequirementName ?? null
  };
}

export function normalizeMyParticipatedProject(
  response: MyParticipatedProjectResponse
): Api.Project.MyParticipatedProjectItem {
  return {
    ...response,
    id: normalizeStringId(response.id),
    code: response.code ?? null,
    statusName: response.statusName ?? null,
    myRole: response.myRole ?? null
  };
}

export function normalizeMyOwnedProject(response: MyOwnedProjectResponse): Api.Project.MyOwnedProjectItem {
  return {
    ...response,
    id: normalizeStringId(response.id),
    code: response.code ?? null,
    myRole: response.myRole ?? null,
    plannedEndDate: response.plannedEndDate ?? null,
    members: (response.members ?? []).map(member => ({
      ...member,
      userId: normalizeStringId(member.userId),
      userName: member.userName ?? null
    }))
  };
}

export function normalizeMyTask(response: MyTaskResponse): Api.Project.MyTaskItem {
  return {
    ...response,
    id: normalizeStringId(response.id),
    projectId: normalizeStringId(response.projectId),
    executionId: normalizeNullableStringId(response.executionId),
    executionName: response.executionName ?? null,
    statusName: response.statusName ?? null,
    priority: normalizePriority(response.priority),
    plannedEndDate: normalizeProjectLocalDate(response.plannedEndDate),
    progressRate: normalizeProgressRate(response.progressRate) ?? 0,
    createTime: normalizeProjectDateTime(response.createTime),
    parentTaskId: normalizeNullableStringId(response.parentTaskId),
    terminal: Boolean(response.terminal),
    allowEdit: Boolean(response.allowEdit),
    availableActions: normalizeLifecycleActions(response.availableActions)
  };
}

function normalizeWorklogDistributionItem(
  response: WorklogDistributionItemResponse | TeamLoadDistributionItemResponse
): { projectId: string | null; projectName: string | null; kind: 'project' | 'personal' | 'other' } {
  return {
    projectId: normalizeNullableStringId(response.projectId),
    projectName: response.projectName ?? null,
    kind: response.kind
  };
}

export function normalizeTeamLoad(response: TeamLoadResponse): Api.Project.TeamLoadResult {
  return {
    members: (response.members ?? []).map(member => ({
      userId: normalizeStringId(member.userId),
      userNickname: member.userNickname ?? '',
      items: (member.items ?? []).map(item => ({
        ...normalizeWorklogDistributionItem(item),
        count: typeof item.count === 'number' ? item.count : 0
      })),
      dueSoonCount: typeof member.dueSoonCount === 'number' ? member.dueSoonCount : 0,
      overdueCount: typeof member.overdueCount === 'number' ? member.overdueCount : 0
    }))
  };
}

export function normalizeMyWorklogWeek(response: MyWorklogWeekResponse): Api.Project.MyWorklogWeekResult {
  return {
    weekStart: response.weekStart ?? '',
    dailyHours: response.dailyHours ?? [0, 0, 0, 0, 0, 0, 0],
    distribution: (response.distribution ?? []).map(item => ({
      ...normalizeWorklogDistributionItem(item),
      hours: typeof item.hours === 'number' ? item.hours : 0
    }))
  };
}

export function normalizeTeamWorklogWeek(response: TeamWorklogWeekResponse): Api.Project.TeamWorklogWeekResult {
  return {
    weekStart: response.weekStart ?? '',
    members: (response.members ?? []).map(member => ({
      userId: normalizeStringId(member.userId),
      userNickname: member.userNickname ?? '',
      overtimeHours: typeof member.overtimeHours === 'number' ? member.overtimeHours : 0,
      items: (member.items ?? []).map(item => ({
        ...normalizeWorklogDistributionItem(item),
        hours: typeof item.hours === 'number' ? item.hours : 0
      }))
    }))
  };
}

export function normalizeExecutionAssignee(response: ExecutionAssigneeResponse): Api.Project.ExecutionAssignee {
  return {
    ...response,
    id: normalizeStringId(response.id),
    executionId: normalizeStringId(response.executionId),
    userId: normalizeStringId(response.userId),
    userNickname: response.userNickname ?? null,
    joinedAt: response.joinedAt ?? null,
    removedAt: response.removedAt ?? null,
    removedReason: response.removedReason ?? null
  };
}

export function normalizeExecutionAssigneeLog(
  response: ExecutionAssigneeLogResponse
): Api.Project.ExecutionAssigneeLog {
  return {
    ...response,
    id: normalizeStringId(response.id),
    executionId: normalizeStringId(response.executionId),
    userId: normalizeStringId(response.userId),
    operatorUserId: normalizeStringId(response.operatorUserId),
    userNicknameSnapshot: response.userNicknameSnapshot ?? null,
    operatorNicknameSnapshot: response.operatorNicknameSnapshot ?? null,
    reason: response.reason ?? null
  };
}

export function normalizeProjectTask(response: ProjectTaskResponse): Api.Project.ProjectTask {
  return {
    ...response,
    id: normalizeStringId(response.id),
    projectId: normalizeStringId(response.projectId),
    executionId: normalizeStringId(response.executionId),
    executionName: response.executionName ?? null,
    executionStatusCode: response.executionStatusCode ?? null,
    parentTaskId: normalizeNullableStringId(response.parentTaskId),
    projectRequirementId: normalizeNullableStringId(response.projectRequirementId),
    projectRequirementName: response.projectRequirementName ?? null,
    projectRequirementStatusCode: response.projectRequirementStatusCode ?? null,
    type: response.type ?? '',
    ownerId: normalizeStringId(response.ownerId),
    ownerNickname: response.ownerNickname ?? null,
    executionOwnerId: normalizeNullableStringId(response.executionOwnerId),
    parentTaskOwnerId: normalizeNullableStringId(response.parentTaskOwnerId),
    statusName: response.statusName ?? null,
    terminal: Boolean(response.terminal),
    allowEdit: Boolean(response.allowEdit),
    availableActions: normalizeLifecycleActions(response.availableActions),
    progressRate: typeof response.progressRate === 'number' ? response.progressRate : 0,
    plannedStartDate: normalizeProjectLocalDate(response.plannedStartDate),
    plannedEndDate: normalizeProjectLocalDate(response.plannedEndDate),
    actualStartDate: normalizeProjectLocalDate(response.actualStartDate),
    actualEndDate: normalizeProjectLocalDate(response.actualEndDate),
    priority: normalizePriority(response.priority),
    priorityName: response.priorityName ?? null,
    taskDesc: response.taskDesc ?? null,
    lastStatusReason: response.lastStatusReason ?? null,
    assignees:
      response.assignees?.map(item => ({
        id: normalizeStringId(item.id),
        userId: normalizeStringId(item.userId),
        nickname: item.nickname ?? ''
      })) ?? null,
    attachments: normalizeAttachments(response.attachments),
    totalSpentHours: response.totalSpentHours ?? null
  };
}

export function normalizeTaskWorklog(response: TaskWorklogResponse): Api.Project.TaskWorklog {
  return {
    ...response,
    id: normalizeStringId(response.id),
    taskId: normalizeStringId(response.taskId),
    userId: normalizeStringId(response.userId),
    userNickname: response.userNickname ?? null,
    workContent: response.workContent ?? null,
    attachments: normalizeAttachments(response.attachments),
    progressRate: typeof response.progressRate === 'number' ? response.progressRate : 0,
    // 后端 LocalDate 默认序列化为 [year, month, day] 数组，必须归一为 'YYYY-MM-DD' 字符串供 ElDatePicker 使用
    startDate: normalizeProjectLocalDate(response.startDate) ?? '',
    endDate: normalizeProjectLocalDate(response.endDate) ?? '',
    // 历史记录或异常缺失时兜底为字典默认档位 "2"
    difficulty: response.difficulty ?? '2',
    difficultyName: response.difficultyName ?? null
  };
}

export function normalizeTaskAssignee(response: TaskAssigneeFromApiResponse): Api.Project.TaskAssigneeRef {
  return {
    id: normalizeStringId(response.id),
    userId: normalizeStringId(response.userId),
    nickname: response.userNickname ?? '',
    joinedAt: response.joinedAt ?? null
  };
}

export function normalizeTaskAssigneeLog(response: TaskAssigneeLogResponse): Api.Project.TaskAssigneeLog {
  return {
    ...response,
    id: normalizeStringId(response.id),
    taskId: normalizeStringId(response.taskId),
    userId: normalizeStringId(response.userId),
    operatorUserId: normalizeStringId(response.operatorUserId),
    userNicknameSnapshot: response.userNicknameSnapshot ?? null,
    operatorNicknameSnapshot: response.operatorNicknameSnapshot ?? null,
    reason: response.reason ?? null
  };
}
