import dayjs from 'dayjs';
import isoWeek from 'dayjs/plugin/isoWeek';
import type { PaginationData } from '@sa/hooks';
import { getStatusTagType } from '@/constants/status-tag';

dayjs.extend(isoWeek);

export type WorkReportType = Api.WorkReport.Common.ReportType;
export type WorkReportRow =
  | Api.WorkReport.Weekly.WeeklyReport
  | Api.WorkReport.Monthly.MonthlyReport
  | Api.WorkReport.Project.ProjectReport;
export type WorkReportSearchParams =
  | Api.WorkReport.Weekly.WeeklyReportSearchParams
  | Api.WorkReport.Monthly.MonthlyReportSearchParams
  | Api.WorkReport.Project.ProjectReportSearchParams;
export type WorkReportSaveParams =
  | Api.WorkReport.Weekly.WeeklyReportSaveParams
  | Api.WorkReport.Monthly.MonthlyReportSaveParams
  | Api.WorkReport.Project.ProjectReportSaveParams;

export interface WorkReportStructuredTask {
  title: string;
  detail?: string;
  priority?: string | null;
  progress?: number | null;
  hours?: number | null;
  kind?: string | null;
}

export interface WorkReportStructuredSection {
  category: string;
  tasks: WorkReportStructuredTask[];
}

export const WORK_REPORT_PROJECT_OWNER_PERMISSION = 'project:work-report:project-owner';

export const WORK_REPORT_TYPE_LABEL: Record<WorkReportType, string> = {
  weekly: '个人周报',
  monthly: '个人月报',
  project: '研发简报'
};

export const TEAM_WORK_REPORT_TYPE_LABEL: Record<WorkReportType, string> = {
  weekly: '团队周报',
  monthly: '团队月报',
  project: '团队研发简报'
};

export function getWorkReportTypeDisplayLabel(reportType: WorkReportType, isTeamMode = false) {
  return isTeamMode ? TEAM_WORK_REPORT_TYPE_LABEL[reportType] : WORK_REPORT_TYPE_LABEL[reportType];
}

export const WORK_REPORT_STATUS_LABEL: Record<string, string> = {
  draft: '待提交',
  pending_approval: '待审批',
  approved: '已通过',
  rejected: '已退回'
};

export const PROJECT_REPORT_FLAG_OPTIONS = [
  { label: '上半月', value: 1 },
  { label: '下半月', value: 2 }
];

export const BOOLEAN_TRUE_FALSE_OPTIONS = [
  { label: '是', value: 'true' },
  { label: '否', value: 'false' }
];

export function getProjectReportFlagLabel(flag?: number | null) {
  return PROJECT_REPORT_FLAG_OPTIONS.find(item => item.value === flag)?.label || '--';
}

export function getWorkReportStatusLabel(statusCode?: string | null, statusName?: string | null) {
  return statusName || WORK_REPORT_STATUS_LABEL[statusCode || ''] || statusCode || '--';
}

export function resolveWorkReportStatusTagType(statusCode?: string | null) {
  return getStatusTagType('workReport', statusCode);
}

export function formatEmptyText(value?: string | number | null) {
  if (value === null || value === undefined || value === '') {
    return '0';
  }

  return String(value);
}

export function formatDate(value?: string | null) {
  return value ? dayjs(value).format('YYYY-MM-DD') : '--';
}

export function formatDateTime(value?: string | null) {
  return value ? dayjs(value).format('YYYY-MM-DD HH:mm') : '--';
}

export function formatPeriodDateRange(
  rowOrStart: Pick<WorkReportRow, 'periodStartDate' | 'periodEndDate'> | string | null | undefined,
  endDate?: string | null
) {
  const startDate = typeof rowOrStart === 'object' && rowOrStart ? rowOrStart.periodStartDate : rowOrStart;
  const rangeEndDate = typeof rowOrStart === 'object' && rowOrStart ? rowOrStart.periodEndDate : endDate;
  const startText = formatDate(startDate);
  const endText = formatDate(rangeEndDate);

  if (startText === '--' && endText === '--') {
    return '--';
  }

  return `${startText} 至 ${endText}`;
}

export function formatWeeklyPeriodLabel(
  rowOrStart: Pick<WorkReportRow, 'periodLabel' | 'periodStartDate' | 'periodEndDate'> | string | null | undefined,
  endDate?: string | null,
  periodLabel?: string | null
) {
  const isRowPayload =
    typeof rowOrStart === 'object' &&
    rowOrStart !== null &&
    !Array.isArray(rowOrStart) &&
    ('periodStartDate' in rowOrStart || 'periodEndDate' in rowOrStart || 'periodLabel' in rowOrStart);
  let startDate: string | null | undefined;
  if (isRowPayload) {
    startDate = rowOrStart.periodStartDate;
  } else if (typeof rowOrStart === 'string') {
    startDate = rowOrStart;
  } else {
    startDate = undefined;
  }
  const rangeEndDate: string | null | undefined = isRowPayload ? rowOrStart.periodEndDate : endDate;
  const fallbackLabel: string | null | undefined = isRowPayload ? rowOrStart.periodLabel : periodLabel;
  const referenceDate = dayjs(startDate || rangeEndDate);

  if (referenceDate.isValid()) {
    const weekYear = referenceDate.startOf('isoWeek').add(3, 'day').format('YYYY');
    return `${weekYear}年第${String(referenceDate.isoWeek()).padStart(2, '0')}周`;
  }

  return formatPeriodLabel(fallbackLabel) || formatPeriodDateRange(startDate, rangeEndDate);
}

export function formatPeriodLabel(value?: string | null) {
  return String(value || '')
    .trim()
    .replace(/\s*(周报|月报|项目汇报|项目半月报|半月报|研发简报)\s*$/u, '');
}

export function formatPeriod(row: Pick<WorkReportRow, 'periodLabel' | 'periodStartDate' | 'periodEndDate'>) {
  return formatPeriodLabel(row.periodLabel) || `${formatDate(row.periodStartDate)} 至 ${formatDate(row.periodEndDate)}`;
}

export function createInitBaseSearchParams() {
  return {
    pageNo: 1,
    pageSize: 10,
    keyword: undefined,
    statusCode: undefined,
    periodStartDate: undefined,
    submitTime: undefined,
    supervisorName: undefined
  };
}

export function createWeeklySearchParams(): Api.WorkReport.Weekly.WeeklyReportSearchParams {
  return {
    ...createInitBaseSearchParams(),
    reporterIds: undefined,
    isBusinessTrip: undefined
  };
}

export function createMonthlySearchParams(): Api.WorkReport.Monthly.MonthlyReportSearchParams {
  return {
    ...createInitBaseSearchParams(),
    reporterIds: undefined
  };
}

export function createProjectSearchParams(): Api.WorkReport.Project.ProjectReportSearchParams {
  return {
    ...createInitBaseSearchParams(),
    projectId: undefined,
    flag: undefined
  };
}

export function transformWorkReportPage<T>(
  response: { data: Api.WorkReport.Common.PageResult<T> | null; error: unknown },
  pageNo: number,
  pageSize: number
): PaginationData<T> {
  if (!response.error && response.data) {
    return {
      data: response.data.list,
      pageNum: pageNo,
      pageSize,
      total: response.data.total
    };
  }

  return {
    data: [],
    pageNum: 1,
    pageSize,
    total: 0
  };
}

export function createBlankReviewItem(index = 0): Api.WorkReport.Common.PersonalReportReviewItem {
  return {
    itemNumber: index + 1,
    itemTitle: '',
    workHours: 0,
    contentText: '',
    contentJson: null,
    reflectionText: ''
  };
}

export function createBlankPlanItem(index = 0): Api.WorkReport.Common.PersonalReportPlanItem {
  return {
    itemNumber: index + 1,
    itemTitle: '',
    targetText: '',
    targetJson: null,
    supportNeed: ''
  };
}

export function createBlankProjectItem(): Api.WorkReport.Project.ProjectReportItem {
  return {
    itemTitle: '',
    workHours: 0,
    priorityCode: undefined,
    progressRate: 0
  };
}

function isRecord(value: unknown): value is Record<string, unknown> {
  return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
}

function normalizeNumber(value: unknown): number | null {
  if (typeof value === 'number' && Number.isFinite(value)) return value;
  if (typeof value === 'string' && value.trim()) {
    const numberValue = Number(value);
    return Number.isFinite(numberValue) ? numberValue : null;
  }
  return null;
}

function normalizeStructuredTask(value: unknown): WorkReportStructuredTask | null {
  if (!isRecord(value)) return null;

  const title = String(value.title ?? value.name ?? '').trim();
  if (!title) return null;

  return {
    title,
    detail: String(value.detail ?? value.content ?? '').trim(),
    priority: value.priority === null || value.priority === undefined ? null : String(value.priority),
    progress: normalizeNumber(value.progress),
    hours: normalizeNumber(value.hours),
    kind: value.kind === null || value.kind === undefined ? null : String(value.kind)
  };
}

function normalizeStructuredSection(value: unknown): WorkReportStructuredSection | null {
  if (!isRecord(value)) return null;

  const category = String(value.category ?? value.title ?? value.name ?? '').trim();
  const rawTasks = Array.isArray(value.tasks) ? value.tasks : [];
  const tasks = rawTasks.map(normalizeStructuredTask).filter(Boolean) as WorkReportStructuredTask[];

  if (!category && !tasks.length) return null;

  return {
    category: category || '工作内容',
    tasks
  };
}

function parseJsonLike(value: unknown): unknown {
  if (typeof value !== 'string') return value;

  try {
    return JSON.parse(value);
  } catch {
    return null;
  }
}

export function getStructuredTasks(value: unknown): WorkReportStructuredTask[] {
  const parsed = parseJsonLike(value);

  if (Array.isArray(parsed)) {
    return parsed.map(normalizeStructuredTask).filter(Boolean) as WorkReportStructuredTask[];
  }

  if (isRecord(parsed) && Array.isArray(parsed.tasks)) {
    return parsed.tasks.map(normalizeStructuredTask).filter(Boolean) as WorkReportStructuredTask[];
  }

  return [];
}

export function getStructuredSections(value: unknown): WorkReportStructuredSection[] {
  const parsed = parseJsonLike(value);

  if (Array.isArray(parsed)) {
    return parsed.map(normalizeStructuredSection).filter(Boolean) as WorkReportStructuredSection[];
  }

  if (isRecord(parsed) && Array.isArray(parsed.sections)) {
    return parsed.sections.map(normalizeStructuredSection).filter(Boolean) as WorkReportStructuredSection[];
  }

  return [];
}

function joinTextValues(...values: Array<string | null | undefined>) {
  return values
    .map(value => value?.trim())
    .filter(Boolean)
    .join('\n');
}

function mergeStructuredSections(sections: WorkReportStructuredSection[]) {
  const sectionMap = new Map<string, WorkReportStructuredSection>();

  sections.forEach(section => {
    const category = section.category || '工作内容';
    const existing = sectionMap.get(category);
    if (existing) {
      existing.tasks.push(...section.tasks);
    } else {
      sectionMap.set(category, { category, tasks: [...section.tasks] });
    }
  });

  return Array.from(sectionMap.values());
}

function mergeStructuredJson(current: unknown, next: unknown) {
  const currentSections = getStructuredSections(current);
  const nextSections = getStructuredSections(next);
  const sections = mergeStructuredSections([...currentSections, ...nextSections]);
  if (sections.length) return JSON.stringify({ sections });

  const tasks = [...getStructuredTasks(current), ...getStructuredTasks(next)];
  if (tasks.length) return JSON.stringify({ tasks });

  return current ?? next ?? null;
}

function groupPersonalReportItemsByTitle<T extends { itemTitle: string }>(
  items: T[],
  merge: (target: T, source: T) => void
) {
  const groupedItems: T[] = [];
  const itemMap = new Map<string, T>();

  items.forEach((item, index) => {
    const title = item.itemTitle.trim();
    const key = title || `__blank_${index}`;
    const existing = itemMap.get(key);

    if (existing) {
      merge(existing, item);
      return;
    }

    itemMap.set(key, item);
    groupedItems.push(item);
  });

  return groupedItems.map((item, index) => ({
    ...item,
    itemNumber: index + 1
  }));
}

export function normalizeReviewItems(items?: Api.WorkReport.Common.PersonalReportReviewItem[] | null) {
  const list = items?.length ? items : [createBlankReviewItem()];

  const normalizedItems = list.map((item, index) => ({
    ...item,
    itemNumber: item.itemNumber ?? index + 1,
    itemTitle: item.itemTitle || '',
    workHours: item.workHours ?? 0,
    contentText: item.contentText || '',
    contentJson: item.contentJson ?? null,
    reflectionText: item.reflectionText || ''
  }));

  return groupPersonalReportItemsByTitle(normalizedItems, (target, source) => {
    target.workHours = Number(target.workHours || 0) + Number(source.workHours || 0);
    target.contentText = joinTextValues(target.contentText, source.contentText);
    target.contentJson = mergeStructuredJson(target.contentJson, source.contentJson);
    target.reflectionText = joinTextValues(target.reflectionText, source.reflectionText);
  });
}

export function normalizePlanItems(items?: Api.WorkReport.Common.PersonalReportPlanItem[] | null) {
  const list = items?.length ? items : [createBlankPlanItem()];

  const normalizedItems = list.map((item, index) => ({
    ...item,
    itemNumber: item.itemNumber ?? index + 1,
    itemTitle: item.itemTitle || '',
    targetText: item.targetText || '',
    targetJson: item.targetJson ?? null,
    supportNeed: item.supportNeed || ''
  }));

  return groupPersonalReportItemsByTitle(normalizedItems, (target, source) => {
    target.targetText = joinTextValues(target.targetText, source.targetText);
    target.targetJson = mergeStructuredJson(target.targetJson, source.targetJson);
    target.supportNeed = joinTextValues(target.supportNeed, source.supportNeed);
  });
}

export function normalizeProjectItems(items?: Api.WorkReport.Project.ProjectReportItem[] | null) {
  const list = items?.length ? items : [createBlankProjectItem()];

  return list.map(item => ({
    ...item,
    itemTitle: item.itemTitle || '',
    workHours: item.workHours ?? 0,
    priorityCode: item.priorityCode || undefined,
    progressRate: item.progressRate ?? 0
  }));
}

export function createWeeklySaveParams(
  base?: Partial<Api.WorkReport.Weekly.WeeklyReportSaveParams>
): Api.WorkReport.Weekly.WeeklyReportSaveParams {
  return {
    periodKey: base?.periodKey || '',
    periodLabel: base?.periodLabel || '',
    periodStartDate: base?.periodStartDate || '',
    periodEndDate: base?.periodEndDate || '',
    isBusinessTrip: base?.isBusinessTrip ?? false,
    reviewItems: normalizeReviewItems(base?.reviewItems),
    planItems: normalizePlanItems(base?.planItems),
    travelSegments: base?.travelSegments?.length ? base.travelSegments : []
  };
}

export function createMonthlySaveParams(
  base?: Partial<Api.WorkReport.Monthly.MonthlyReportSaveParams>
): Api.WorkReport.Monthly.MonthlyReportSaveParams {
  return {
    periodKey: base?.periodKey || '',
    periodLabel: base?.periodLabel || '',
    periodStartDate: base?.periodStartDate || '',
    periodEndDate: base?.periodEndDate || '',
    reviewItems: normalizeReviewItems(base?.reviewItems),
    planItems: normalizePlanItems(base?.planItems)
  };
}

export function createProjectSaveParams(
  base?: Partial<Api.WorkReport.Project.ProjectReportSaveParams>
): Api.WorkReport.Project.ProjectReportSaveParams {
  const defaultParams: Api.WorkReport.Project.ProjectReportSaveParams = {
    projectId: '',
    periodKey: '',
    periodLabel: '',
    periodStartDate: '',
    periodEndDate: '',
    flag: undefined,
    projectStatusDesc: '',
    projectProgressPlan: '',
    projectKeyPoints: '',
    projectProblems: '',
    currentItems: [createBlankProjectItem()],
    nextItems: [createBlankProjectItem()]
  };

  return {
    ...Object.assign(defaultParams, base),
    currentItems: normalizeProjectItems(base?.currentItems),
    nextItems: normalizeProjectItems(base?.nextItems)
  };
}

type NavigatorWithLegacySave = Navigator & {
  msSaveOrOpenBlob?: (blob: Blob, defaultName?: string) => boolean;
};

export function downloadBlob(blob: Blob, filename: string) {
  if (!(blob instanceof Blob)) {
    window.$message?.error(getBlobErrorMessage(blob) || '导出失败：接口未返回文件流');
    return;
  }

  const downloadFile =
    blob instanceof File ? blob : new File([blob], filename, { type: blob.type || 'application/octet-stream' });
  const legacyNavigator = window.navigator as NavigatorWithLegacySave;

  if (typeof legacyNavigator.msSaveOrOpenBlob === 'function') {
    legacyNavigator.msSaveOrOpenBlob(downloadFile, filename);
    window.$message?.success('导出文件已开始下载');
    return;
  }

  const url = window.URL.createObjectURL(downloadFile);
  const link = document.createElement('a');
  link.href = url;
  link.download = filename;
  link.style.display = 'none';
  link.rel = 'noopener';

  document.body.appendChild(link);
  link.click();
  document.body.removeChild(link);

  window.setTimeout(() => {
    window.URL.revokeObjectURL(url);
  }, 1000);
  window.$message?.success('导出文件已开始下载');
}

function getBlobErrorMessage(value: unknown) {
  if (!isRecord(value)) return '';

  const message = value.msg || value.message || value.error;
  return typeof message === 'string' && message.trim() ? message.trim() : '';
}

function safeDecodeFilename(value: string) {
  try {
    return decodeURIComponent(value);
  } catch {
    return value;
  }
}

function getResponseHeader(headers: unknown, headerName: string) {
  if (!headers) return '';

  if (typeof (headers as { get?: unknown }).get === 'function') {
    const value = (headers as { get: (name: string) => unknown }).get(headerName);
    return value === null || value === undefined ? '' : String(value);
  }

  if (!isRecord(headers)) return '';

  const matchedKey = Object.keys(headers).find(key => key.toLowerCase() === headerName.toLowerCase());
  if (!matchedKey) return '';

  const value = headers[matchedKey];
  return value === null || value === undefined ? '' : String(value);
}

export function getFilenameFromDisposition(disposition?: string | null) {
  if (!disposition) return '';

  const filenameStarMatch = disposition.match(/filename\*=UTF-8''([^;]+)/i);
  if (filenameStarMatch?.[1]) {
    return safeDecodeFilename(filenameStarMatch[1].replace(/^"|"$/g, ''));
  }

  const filenameMatch = disposition.match(/filename="?([^";]+)"?/i);
  if (filenameMatch?.[1]) {
    return safeDecodeFilename(filenameMatch[1]);
  }

  return '';
}

export function resolveExportFilename(result: { response?: { headers?: unknown } }, fallbackName: string) {
  const disposition = getResponseHeader(result.response?.headers, 'content-disposition');
  return getFilenameFromDisposition(disposition) || fallbackName;
}

export function createWorkReportContentExportFallbackName(reportType: WorkReportType, reportCount: number) {
  const extension = reportCount === 1 ? 'docx' : 'zip';
  return `${WORK_REPORT_TYPE_LABEL[reportType]}_${dayjs().format('YYYY-MM-DD')}.${extension}`;
}
