export type PriorityLevel = "高" | "中" | "低";
export type SeverityLevel = "高" | "中" | "低";
export type RequirementType =
  | "functional"
  | "interface"
  | "performance"
  | "security"
  | "reliability"
  | "other";

export interface RequirementItem {
  id: string;
  title?: string;
  description: string;
  priority: PriorityLevel;
  acceptanceCriteria: string[];
  sourceField: string;
  sectionUid?: string;
  sectionNumber?: string;
  sectionTitle?: string;
  requirementType?: RequirementType;
  interfaceName?: string;
  interfaceType?: string;
  dataSource?: string;
  dataDestination?: string;
  sortOrder?: number;
}

export interface RequirementExtractionResult {
  documentName: string;
  generatedAt: string;
  requirements: RequirementItem[];
  rawOutput?: Record<string, unknown>;
}

export interface TestCaseItem {
  id: string;
  requirementId: string;
  requirementTitle: string;
  title: string;
  preconditions: string[];
  steps: string[];
  expectedResults: string[];
  priority: PriorityLevel;
  tags: string[];
}

export interface TestCaseGenerationResult {
  sourceDocument: string;
  generatedAt: string;
  testCases: TestCaseItem[];
}

export interface ConsistencyIssue {
  id: string;
  type: string;
  severity: SeverityLevel;
  requirementRef: string;
  codeRef: string;
  summary: string;
  suggestion: string;
}

export interface ConsistencyReport {
  requirementFileName: string;
  codeFileName: string;
  generatedAt: string;
  consistencyScore: number;
  coverage: number;
  issues: ConsistencyIssue[];
}

const STORAGE_KEYS = {
  extractionDraft: "doc_processing_extraction_draft",
  testCaseDraft: "doc_processing_test_case_draft",
  consistencyDraft: "consistency_analysis_draft",
};

const WAIT_TIME = {
  extract: 950,
  generateCases: 900,
  analyze: 1100,
};

const asPriority = (value: unknown): PriorityLevel => {
  if (value === "高" || value === "中" || value === "低") {
    return value;
  }
  return "中";
};

const asRequirementType = (value: unknown): RequirementType => {
  if (
    value === "functional" ||
    value === "interface" ||
    value === "performance" ||
    value === "security" ||
    value === "reliability" ||
    value === "other"
  ) {
    return value;
  }
  if (value === "接口需求") {
    return "interface";
  }
  if (value === "性能需求") {
    return "performance";
  }
  if (value === "安全需求") {
    return "security";
  }
  if (value === "可靠性需求") {
    return "reliability";
  }
  if (value === "其他需求") {
    return "other";
  }
  return "functional";
};

const summarizeTitle = (value: string, fallbackIndex: number) => {
  const source = (value || "").trim();
  if (!source) {
    return `需求项 ${fallbackIndex + 1}`;
  }

  for (const separator of ["。", "；", "\n", ";", "."]) {
    if (source.includes(separator)) {
      const first = source.split(separator, 1)[0]?.trim();
      if (first) {
        return first.slice(0, 20);
      }
    }
  }
  return source.slice(0, 20);
};

const asSeverity = (value: unknown): SeverityLevel => {
  if (value === "高" || value === "中" || value === "低") {
    return value;
  }
  return "中";
};

const wait = (ms: number) =>
  new Promise<void>((resolve) => {
    setTimeout(resolve, ms);
  });

const hashText = (value: string) => {
  let hash = 0;
  for (let i = 0; i < value.length; i += 1) {
    hash = (hash * 31 + value.charCodeAt(i)) >>> 0;
  }
  return hash;
};

const seededPick = <T>(items: T[], seed: number, offset = 0): T => {
  return items[(seed + offset) % items.length];
};

const toIso = () => new Date().toISOString();

const safeStorage = {
  set<T>(key: string, value: T) {
    if (typeof window === "undefined") {
      return;
    }
    window.localStorage.setItem(key, JSON.stringify(value));
  },
  get<T>(key: string): T | null {
    if (typeof window === "undefined") {
      return null;
    }

    const raw = window.localStorage.getItem(key);
    if (!raw) {
      return null;
    }

    try {
      return JSON.parse(raw) as T;
    } catch {
      return null;
    }
  },
};

const normalizeRequirementItem = (
  item: Partial<RequirementItem>,
  fallbackIndex: number
): RequirementItem => {
  const acceptanceCriteria = Array.isArray(item.acceptanceCriteria)
    ? item.acceptanceCriteria.map((entry) => String(entry))
    : [];

  return {
    id:
      typeof item.id === "string" && item.id.trim().length > 0
        ? item.id
        : `REQ-${String(fallbackIndex + 1).padStart(3, "0")}`,
    title:
      typeof item.title === "string" && item.title.trim().length > 0
        ? item.title
        : undefined,
    description:
      typeof item.description === "string" ? item.description : "",
    priority: asPriority(item.priority),
    acceptanceCriteria:
      acceptanceCriteria.length > 0 ? acceptanceCriteria : ["待补充验收标准"],
    sourceField:
      typeof item.sourceField === "string" && item.sourceField.trim().length > 0
        ? item.sourceField
        : `章节 ${fallbackIndex + 1}`,
    sectionUid:
      typeof item.sectionUid === "string" ? item.sectionUid : undefined,
    sectionNumber:
      typeof item.sectionNumber === "string" ? item.sectionNumber : undefined,
    sectionTitle:
      typeof item.sectionTitle === "string" ? item.sectionTitle : undefined,
    requirementType: asRequirementType(item.requirementType),
    interfaceName:
      typeof item.interfaceName === "string" ? item.interfaceName : "",
    interfaceType:
      typeof item.interfaceType === "string" ? item.interfaceType : "",
    dataSource:
      typeof item.dataSource === "string" ? item.dataSource : "",
    dataDestination:
      typeof item.dataDestination === "string" ? item.dataDestination : "",
    sortOrder:
      typeof item.sortOrder === "number" && Number.isFinite(item.sortOrder)
        ? item.sortOrder
        : fallbackIndex,
  };
};

const buildRawOutputFromRequirements = (
  requirements: RequirementItem[],
  documentName: string,
  generatedAt: string
): Record<string, unknown> => {
  const sectionMap = new Map<
    string,
    {
      sectionNumber: string;
      sectionTitle: string;
      list: Array<Record<string, unknown>>;
    }
  >();

  const byType: Record<string, number> = {};

  requirements.forEach((req) => {
    const number = req.sectionNumber || "";
    const title = req.sectionTitle || "未归类章节";
    const key = `${number}__${title}`;
    if (!sectionMap.has(key)) {
      sectionMap.set(key, {
        sectionNumber: number,
        sectionTitle: title,
        list: [],
      });
    }

    const reqType = asRequirementType(req.requirementType);
    const typeLabel =
      reqType === "interface"
        ? "接口需求"
        : reqType === "performance"
          ? "性能需求"
          : reqType === "security"
            ? "安全需求"
            : reqType === "reliability"
              ? "可靠性需求"
              : reqType === "other"
                ? "其他需求"
                : "功能需求";
    byType[typeLabel] = (byType[typeLabel] || 0) + 1;

    const reqEntry: Record<string, unknown> = {
      需求类型: typeLabel,
      需求编号: req.id,
      需求描述: req.description,
      优先级: req.priority || "中",
    };
    if (reqType === "interface") {
      reqEntry["接口名称"] = req.interfaceName || "";
      reqEntry["接口类型"] = req.interfaceType || "";
      reqEntry["数据来源"] = req.dataSource || "";
      reqEntry["数据目的地"] = req.dataDestination || "";
    }

    sectionMap.get(key)?.list.push(reqEntry);
  });

  const content: Record<string, unknown> = {};
  for (const section of sectionMap.values()) {
    const display = `${section.sectionNumber} ${section.sectionTitle}`.trim();
    content[display || "未归类章节"] = {
      章节信息: {
        章节编号: section.sectionNumber,
        章节标题: section.sectionTitle,
        章节级别: section.sectionNumber
          ? section.sectionNumber.split(".").length
          : 1,
      },
      需求列表: section.list,
    };
  }

  return {
    文档元数据: {
      标题: documentName,
      生成时间: generatedAt,
      总需求数: requirements.length,
      需求类型统计: byType,
    },
    需求内容: content,
  };
};

const parseRawOutputRequirements = (parsed: Record<string, unknown>) => {
  const metadata =
    parsed["文档元数据"] && typeof parsed["文档元数据"] === "object"
      ? (parsed["文档元数据"] as Record<string, unknown>)
      : {};
  const content =
    parsed["需求内容"] && typeof parsed["需求内容"] === "object"
      ? (parsed["需求内容"] as Record<string, unknown>)
      : null;

  if (!content) {
    throw new Error("JSON 中缺少 需求内容 字段");
  }

  const requirements: RequirementItem[] = [];

  const walk = (node: unknown) => {
    if (!node || typeof node !== "object") {
      return;
    }
    const sectionNode = node as Record<string, unknown>;
    const sectionInfo =
      sectionNode["章节信息"] && typeof sectionNode["章节信息"] === "object"
        ? (sectionNode["章节信息"] as Record<string, unknown>)
        : {};
    const sectionNumber =
      typeof sectionInfo["章节编号"] === "string" ? sectionInfo["章节编号"] : "";
    const sectionTitle =
      typeof sectionInfo["章节标题"] === "string"
        ? sectionInfo["章节标题"]
        : "未归类章节";
    const sectionUid =
      typeof sectionInfo["章节UID"] === "string" ? sectionInfo["章节UID"] : undefined;

    const reqList = Array.isArray(sectionNode["需求列表"])
      ? (sectionNode["需求列表"] as Array<Record<string, unknown>>)
      : [];

    reqList.forEach((req, index) => {
      const description =
        typeof req["需求描述"] === "string" ? req["需求描述"] : "";
      const reqType = asRequirementType(req["需求类型"]);
      requirements.push(
        normalizeRequirementItem(
          {
            id:
              typeof req["需求编号"] === "string" && req["需求编号"].trim().length > 0
                ? req["需求编号"]
                : undefined,
            title:
              typeof req["需求标题"] === "string" && req["需求标题"].trim().length > 0
                ? req["需求标题"]
                : undefined,
            description,
            priority: asPriority(req["优先级"]),
            acceptanceCriteria: description ? [description] : ["待补充验收标准"],
            sourceField: `${sectionNumber} ${sectionTitle}`.trim() || "文档解析",
            sectionUid,
            sectionNumber,
            sectionTitle,
            requirementType: reqType,
            interfaceName:
              reqType === "interface" && typeof req["接口名称"] === "string"
                ? req["接口名称"]
                : "",
            interfaceType:
              reqType === "interface" && typeof req["接口类型"] === "string"
                ? req["接口类型"]
                : "",
            dataSource:
              reqType === "interface" && typeof req["数据来源"] === "string"
                ? req["数据来源"]
                : "",
            dataDestination:
              reqType === "interface" && typeof req["数据目的地"] === "string"
                ? req["数据目的地"]
                : "",
            sortOrder: requirements.length + index,
          },
          requirements.length + index
        )
      );
    });

    const children =
      sectionNode["子章节"] && typeof sectionNode["子章节"] === "object"
        ? (sectionNode["子章节"] as Record<string, unknown>)
        : null;

    if (!children) {
      return;
    }
    Object.values(children).forEach((child) => walk(child));
  };

  Object.values(content).forEach((section) => walk(section));

  const generatedAt =
    typeof metadata["生成时间"] === "string" && metadata["生成时间"].trim().length > 0
      ? metadata["生成时间"]
      : toIso();
  const documentName =
    typeof metadata["标题"] === "string" && metadata["标题"].trim().length > 0
      ? metadata["标题"]
      : "导入需求文件";

  return {
    documentName,
    generatedAt,
    requirements,
    rawOutput: parsed,
  } as RequirementExtractionResult;
};

export const parseRequirementJson = (
  content: string
): RequirementExtractionResult => {
  const parsed = JSON.parse(content) as unknown;

  if (!parsed || typeof parsed !== "object") {
    throw new Error("JSON 内容无效，请检查文件格式");
  }

  const value = parsed as {
    documentName?: unknown;
    generatedAt?: unknown;
    requirements?: unknown;
    [key: string]: unknown;
  };

  if (
    value["需求内容"] &&
    typeof value["需求内容"] === "object" &&
    value["文档元数据"] &&
    typeof value["文档元数据"] === "object"
  ) {
    return parseRawOutputRequirements(value as Record<string, unknown>);
  }

  if (!Array.isArray(value.requirements)) {
    throw new Error("JSON 中缺少 requirements 数组字段");
  }

  const requirements = value.requirements.map((entry, index) =>
    normalizeRequirementItem((entry as Partial<RequirementItem>) ?? {}, index)
  );

  return {
    documentName:
      typeof value.documentName === "string" && value.documentName.trim().length > 0
        ? value.documentName
        : "导入需求文件",
    generatedAt:
      typeof value.generatedAt === "string" && value.generatedAt.trim().length > 0
        ? value.generatedAt
        : toIso(),
    requirements,
    rawOutput: buildRawOutputFromRequirements(
      requirements,
      typeof value.documentName === "string" && value.documentName.trim().length > 0
        ? value.documentName
        : "导入需求文件",
      typeof value.generatedAt === "string" && value.generatedAt.trim().length > 0
        ? value.generatedAt
        : toIso()
    ),
  };
};

export const mockExtractRequirements = async (
  file: File
): Promise<RequirementExtractionResult> => {
  await wait(WAIT_TIME.extract);

  const seed = hashText(file.name);
  const moduleCandidates = ["认证", "检索", "会话", "权限", "日志", "导出"];
  const actionCandidates = [
    "支持配置化执行",
    "提供异常处理提示",
    "可追踪关键链路",
    "支持批量操作",
    "支持结果回溯",
    "具备可编辑能力",
  ];
  const priorities: PriorityLevel[] = ["高", "中", "低"];

  const requirements: RequirementItem[] = Array.from({ length: 5 }).map(
    (_, index) => {
      const moduleName = seededPick(moduleCandidates, seed, index);
      const action = seededPick(actionCandidates, seed, index + 3);
      const requirementNumber = String(index + 1).padStart(3, "0");

      return {
        id: `REQ-${requirementNumber}`,
        title: `${moduleName}能力需求 ${index + 1}`,
        description: `系统在${moduleName}场景下应${action}，并确保操作过程可观测。`,
        priority: seededPick(priorities, seed, index),
        acceptanceCriteria: [
          `当用户完成输入后，${moduleName}流程可在 2 秒内给出反馈。`,
          `在错误条件下，页面需提供明确提示并保留用户已输入信息。`,
          `处理结果必须可以导出并支持后续追踪。`,
        ],
        sourceField: `文档片段 ${index + 1}`,
      };
    }
  );

  return {
    documentName: file.name,
    generatedAt: toIso(),
    requirements,
  };
};

export const mockGenerateTestCases = async (
  extraction: RequirementExtractionResult
): Promise<TestCaseGenerationResult> => {
  await wait(WAIT_TIME.generateCases);

  const cases: TestCaseItem[] = extraction.requirements.map((item, index) => {
    const displayTitle = item.title || summarizeTitle(item.description, index);
    return {
      id: `TC-${String(index + 1).padStart(3, "0")}`,
      requirementId: item.id,
      requirementTitle: displayTitle,
      title: `${displayTitle} - 功能验证`,
      preconditions: [
        "系统已启动并完成账号登录",
        "测试数据准备完毕",
        "目标模块已开启对应配置",
      ],
      steps: [
        `进入 ${item.sourceField} 对应功能页面`,
        "输入合法数据并提交",
        "观察页面反馈与状态变化",
        "触发一次异常输入场景",
        "再次执行提交并确认恢复能力",
      ],
      expectedResults: [
        "系统返回成功提示且状态更新正确",
        "异常场景出现清晰错误提示，不会清空已填内容",
        "操作日志可追踪，结果可导出",
      ],
      priority: item.priority,
      tags: ["自动生成", "需求映射", item.sourceField],
    };
  });

  return {
    sourceDocument: extraction.documentName,
    generatedAt: toIso(),
    testCases: cases,
  };
};

export const mockAnalyzeConsistency = async (
  requirementFile: File,
  codeFile: File
): Promise<ConsistencyReport> => {
  await wait(WAIT_TIME.analyze);

  const seed = hashText(`${requirementFile.name}-${codeFile.name}`);
  const issueTemplates = [
    {
      type: "需求未覆盖",
      summary: "需求中存在关键行为，但代码中缺少对应分支。",
      suggestion: "补充对应功能分支并增加单元测试。",
    },
    {
      type: "实现偏差",
      summary: "代码行为与需求描述不一致，存在参数约束差异。",
      suggestion: "统一需求与代码中的参数规则，并补充说明。",
    },
    {
      type: "验收标准缺失",
      summary: "需求定义了验收口径，但代码未体现可验证输出。",
      suggestion: "增加可验证输出字段，并补充验证逻辑。",
    },
    {
      type: "异常处理不足",
      summary: "需求要求异常可恢复，代码当前只记录日志未提示用户。",
      suggestion: "增加用户可见提示，并保留现场数据。",
    },
  ];

  const severityLevels: SeverityLevel[] = ["高", "中", "低"];
  const issueCount = 3 + (seed % 3);

  const issues: ConsistencyIssue[] = Array.from({ length: issueCount }).map(
    (_, index) => {
      const template = seededPick(issueTemplates, seed, index);
      return {
        id: `ISSUE-${String(index + 1).padStart(3, "0")}`,
        type: template.type,
        severity: seededPick(severityLevels, seed, index + 2),
        requirementRef: `REQ-${String(index + 1).padStart(3, "0")}`,
        codeRef: `${codeFile.name}:L${20 + index * 17}`,
        summary: template.summary,
        suggestion: template.suggestion,
      };
    }
  );

  const consistencyScore = Math.min(95, 70 + (seed % 20));
  const coverage = Math.min(96, 68 + ((seed >> 3) % 24));

  return {
    requirementFileName: requirementFile.name,
    codeFileName: codeFile.name,
    generatedAt: toIso(),
    consistencyScore,
    coverage,
    issues,
  };
};

export const toWordContent = (result: TestCaseGenerationResult) => {
  const lines: string[] = [];

  lines.push(`测试用例生成结果`);
  lines.push(`来源需求文件: ${result.sourceDocument}`);
  lines.push(`生成时间: ${new Date(result.generatedAt).toLocaleString("zh-CN")}`);
  lines.push("");

  result.testCases.forEach((testCase, index) => {
    lines.push(`${index + 1}. ${testCase.id} ${testCase.title}`);
    lines.push(`需求映射: ${testCase.requirementId} ${testCase.requirementTitle}`);
    lines.push(`优先级: ${testCase.priority}`);
    lines.push(`标签: ${testCase.tags.join("、")}`);
    lines.push("前置条件:");
    testCase.preconditions.forEach((line, i) => {
      lines.push(`  ${i + 1}) ${line}`);
    });
    lines.push("步骤:");
    testCase.steps.forEach((line, i) => {
      lines.push(`  ${i + 1}) ${line}`);
    });
    lines.push("预期结果:");
    testCase.expectedResults.forEach((line, i) => {
      lines.push(`  ${i + 1}) ${line}`);
    });
    lines.push("");
  });

  return lines.join("\n");
};

const downloadBlob = (fileName: string, blob: Blob) => {
  if (typeof window === "undefined") {
    return;
  }

  const url = window.URL.createObjectURL(blob);
  const link = document.createElement("a");
  link.href = url;
  link.download = fileName;
  document.body.appendChild(link);
  link.click();
  document.body.removeChild(link);
  window.URL.revokeObjectURL(url);
};

export const downloadJson = (fileName: string, data: unknown) => {
  const json = JSON.stringify(data, null, 2);
  const blob = new Blob([json], { type: "application/json;charset=utf-8" });
  downloadBlob(fileName, blob);
};

export const downloadWord = (fileName: string, content: string) => {
  const blob = new Blob([content], { type: "application/msword;charset=utf-8" });
  downloadBlob(fileName, blob);
};

export const saveExtractionDraft = (data: RequirementExtractionResult) => {
  safeStorage.set(STORAGE_KEYS.extractionDraft, data);
};

export const loadExtractionDraft = () => {
  const value = safeStorage.get<RequirementExtractionResult>(
    STORAGE_KEYS.extractionDraft
  );
  if (!value || !Array.isArray(value.requirements)) {
    return null;
  }

  return {
    ...value,
    requirements: value.requirements.map((item, index) =>
      normalizeRequirementItem(item, index)
    ),
    rawOutput:
      value.rawOutput && typeof value.rawOutput === "object"
        ? value.rawOutput
        : buildRawOutputFromRequirements(
            value.requirements.map((item, index) =>
              normalizeRequirementItem(item, index)
            ),
            value.documentName,
            value.generatedAt
          ),
  };
};

export const saveTestCaseDraft = (data: TestCaseGenerationResult) => {
  safeStorage.set(STORAGE_KEYS.testCaseDraft, data);
};

export const loadTestCaseDraft = () => {
  const value = safeStorage.get<TestCaseGenerationResult>(
    STORAGE_KEYS.testCaseDraft
  );
  if (!value || !Array.isArray(value.testCases)) {
    return null;
  }
  return value;
};

export const saveConsistencyDraft = (data: ConsistencyReport) => {
  safeStorage.set(STORAGE_KEYS.consistencyDraft, data);
};

export const loadConsistencyDraft = () => {
  const value = safeStorage.get<ConsistencyReport>(STORAGE_KEYS.consistencyDraft);
  if (!value || !Array.isArray(value.issues)) {
    return null;
  }

  return {
    ...value,
    issues: value.issues.map((issue) => ({
      ...issue,
      severity: asSeverity(issue.severity),
    })),
  };
};
