import { createHash, randomUUID } from "node:crypto";
import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
import path from "node:path";
import type { TaskPanelArtifact, TaskPanelItem } from "@qjclaw/shared-types";

interface TaskPanelState {
  itemsByDate: Record<string, TaskPanelItem[]>;
}

export interface RecordWorkspaceExecutionInput {
  sessionId: string;
  runId: string;
  date?: string;
  expertName: string;
  taskTitle: string;
  completedAt?: string;
  messageCount?: number;
  artifacts: TaskPanelArtifact[];
}

const VALID_DATE_PATTERN = /^\d{4}-\d{2}-\d{2}$/;

function createEmptyState(): TaskPanelState {
  return {
    itemsByDate: {}
  };
}

function hashValue(value: string): string {
  return createHash("sha256").update(value).digest("hex").slice(0, 16);
}

function normalizeArtifactKey(artifact: TaskPanelArtifact): string {
  const key = artifact.path?.trim() || artifact.url?.trim() || artifact.name.trim() || artifact.id;
  return key.replace(/[\\/]+/g, "/").toLowerCase();
}

function dedupeArtifacts(artifacts: TaskPanelArtifact[]): TaskPanelArtifact[] {
  const seen = new Set<string>();
  const deduped: TaskPanelArtifact[] = [];

  for (const artifact of artifacts) {
    const key = normalizeArtifactKey(artifact);
    if (!key || seen.has(key)) {
      continue;
    }
    seen.add(key);
    deduped.push({
      ...artifact,
      id: artifact.id || "artifact-" + hashValue(key),
      url: artifact.url ?? artifact.path
    });
  }

  return deduped;
}

function toLocalDateInputValue(date: Date): string {
  const year = date.getFullYear();
  const month = String(date.getMonth() + 1).padStart(2, "0");
  const day = String(date.getDate()).padStart(2, "0");
  return `${year}-${month}-${day}`;
}

function toLocalTimeValue(date: Date): string {
  const hour = String(date.getHours()).padStart(2, "0");
  const minute = String(date.getMinutes()).padStart(2, "0");
  const second = String(date.getSeconds()).padStart(2, "0");
  return `${hour}:${minute}:${second}`;
}

function parseCompletedAt(value?: string): Date {
  const date = value ? new Date(value) : new Date();
  return Number.isNaN(date.getTime()) ? new Date() : date;
}

function isTaskPanelState(value: unknown): value is TaskPanelState {
  return Boolean(
    value
      && typeof value === "object"
      && !Array.isArray(value)
      && typeof (value as { itemsByDate?: unknown }).itemsByDate === "object"
      && !Array.isArray((value as { itemsByDate?: unknown }).itemsByDate)
  );
}

export class TaskPanelService {
  private readonly statePath: string;
  private writeChain: Promise<unknown> = Promise.resolve();

  constructor(userDataPath: string) {
    this.statePath = path.join(userDataPath, "task-panel", "state.json");
  }

  async listByDate(date: string): Promise<TaskPanelItem[]> {
    if (!VALID_DATE_PATTERN.test(date)) {
      return [];
    }

    const state = await this.loadState();
    return [...(state.itemsByDate[date] ?? [])];
  }

  async recordWorkspaceExecution(input: RecordWorkspaceExecutionInput): Promise<TaskPanelItem | null> {
    const operation = this.writeChain
      .catch(() => undefined)
      .then(() => this.recordWorkspaceExecutionUnlocked(input));
    this.writeChain = operation;
    return operation;
  }

  private async recordWorkspaceExecutionUnlocked(input: RecordWorkspaceExecutionInput): Promise<TaskPanelItem | null> {
    const completedAt = parseCompletedAt(input.completedAt);
    const date = input.date && VALID_DATE_PATTERN.test(input.date)
      ? input.date
      : toLocalDateInputValue(completedAt);
    const runKey = `${input.sessionId.trim()}::${input.runId.trim() || randomUUID()}`;
    const itemId = "task-" + hashValue(runKey);
    const artifacts = dedupeArtifacts(input.artifacts);
    const nextItem: TaskPanelItem = {
      id: itemId,
      date,
      expertName: input.expertName,
      taskTitle: input.taskTitle,
      status: "completed",
      statusDetail: artifacts.length ? `已完成，识别到 ${artifacts.length} 个产物` : "已完成",
      creditsUsed: 0,
      messageCount: input.messageCount ?? 2,
      updatedAt: toLocalTimeValue(completedAt),
      artifacts
    };

    const state = await this.loadState();
    const items = [...(state.itemsByDate[date] ?? [])];
    const existingIndex = items.findIndex((item) => item.id === itemId);
    if (existingIndex >= 0) {
      const existing = items[existingIndex]!;
      nextItem.artifacts = dedupeArtifacts([...existing.artifacts, ...nextItem.artifacts]);
      nextItem.statusDetail = nextItem.artifacts.length
        ? `已完成，识别到 ${nextItem.artifacts.length} 个产物`
        : "已完成";
      items[existingIndex] = nextItem;
    } else {
      items.push(nextItem);
    }

    state.itemsByDate[date] = items.sort((left, right) => {
      const leftKey = `${left.date} ${left.updatedAt ?? ""}`;
      const rightKey = `${right.date} ${right.updatedAt ?? ""}`;
      return rightKey.localeCompare(leftKey);
    });
    await this.saveState(state);
    return nextItem;
  }

  private async loadState(): Promise<TaskPanelState> {
    try {
      const raw = await readFile(this.statePath, "utf8");
      const parsed = JSON.parse(raw) as unknown;
      return isTaskPanelState(parsed) ? parsed : createEmptyState();
    } catch {
      return createEmptyState();
    }
  }

  private async saveState(state: TaskPanelState): Promise<void> {
    await mkdir(path.dirname(this.statePath), { recursive: true });
    const tempPath = this.statePath + ".tmp";
    await writeFile(tempPath, JSON.stringify(state, null, 2), "utf8");
    await rename(tempPath, this.statePath);
  }
}
