import { parseGrl } from "../grl/parser/index.js";
import { compileSemanticProgram } from "../grl/semantic/index.js";
import { postProcessAllBrands } from "../grl/post/index.js";
import { importBrandProgram } from "../importers/index.js";
import type { MotionDiagnostic } from "../kdl/types.js";
import { createValidationReport, createCustomerDeliveryPackage, renderValidationReportHtml } from "../reports/index.js";
import { createOlpProject, type OlpProjectModel } from "../olp/index.js";
import {
  ABB120_ROBOT_FIXTURE,
  ABB_IRB120_ZERO_JOINTS
} from "../fixtures/abb120.js";

export interface Abb120SuiteProgram {
  name: string;
  text: string;
}

export interface Abb120SuiteJob {
  job_id: string;
  robot: typeof ABB120_ROBOT_FIXTURE;
  programs: Array<{
    name: string;
    moduleName: string;
    diagnostics: MotionDiagnostic[];
    astKind: "Program";
    sourceMapEntries: number;
    kdlMotionRequests: number;
    kdlPathRequests: number;
  }>;
  post: {
    filenames: string[];
    report: Array<{ code: string; severity: string; message: string }>;
  };
  roundtrip: {
    status: "pass" | "warn" | "fail";
    diagnostics: MotionDiagnostic[];
  };
  report_id: string;
  artifacts: {
    jobJson: string;
    traceJson: string;
    trajectoryJson: string;
    diagnosticsJson: string;
    reportJson: string;
    reportHtml: string;
    deliveryPackageJson: string;
    desktopScreenshot: string;
    mobileScreenshot: string;
  };
}

export function createAbb120OlpProject(sourceProgram: Abb120SuiteProgram): OlpProjectModel {
  const project = createOlpProject({ id: "abb120_suite", name: "Abb120Suite", customer: "KDL" });
  project.station = {
    id: "abb120_station",
    name: "ABB120 Station",
    resourceIds: ["abb_irb120_3_58", "tool0", "world"],
    activeRobotIds: ["abb_irb120_3_58"],
    defaultFrameId: "world"
  };
  project.resources.robots.push({
    id: "abb_irb120_3_58",
    name: "ABB IRB120 3/58",
    kind: "robot",
    brand: "abb",
    model: "IRB120 3/58",
    dof: 6,
    jointNames: ["joint_1", "joint_2", "joint_3", "joint_4", "joint_5", "joint_6"],
    limits: [
      { name: "joint_1", lower: -2.87979, upper: 2.87979, velocity: 4.36332 },
      { name: "joint_2", lower: -1.91986, upper: 1.91986, velocity: 4.36332 },
      { name: "joint_3", lower: -1.91986, upper: 1.22173, velocity: 4.36332 },
      { name: "joint_4", lower: -2.79253, upper: 2.79253, velocity: 5.58505 },
      { name: "joint_5", lower: -2.094395, upper: 2.094395, velocity: 5.58505 },
      { name: "joint_6", lower: -6.98132, upper: 6.98132, velocity: 7.33038 }
    ],
    baseFrameId: "world",
    controller: { family: "IRC5/OmniCore virtual" }
  });
  project.resources.tools.push({ id: "tool0", name: "tool0", kind: "tool", tcp: [0, 0, 0, 0, 0, 0], robotId: "abb_irb120_3_58" });
  project.resources.frames.push({ id: "world", name: "world", kind: "frame", pose: [0, 0, 0, 0, 0, 0] });
  project.targets.push({ id: "home", name: "home", kind: "joint", robotId: "abb_irb120_3_58", joints: [...ABB_IRB120_ZERO_JOINTS] });
  project.speeds.push({ id: "v_joint", name: "v_joint", kind: "joint_percent", value: 40 });
  project.zones.push({ id: "zf", name: "zf", kind: "fine" });
  project.programs.push({
    id: sourceProgram.name.replace(/\.grl$/i, ""),
    name: sourceProgram.name,
    language: "grl",
    entryOperationIds: [],
    source: { kind: "authored", text: sourceProgram.text }
  });
  project.calibrations.push({
    id: "tcp_cal_tool0",
    name: "tool0_tcp_cal",
    kind: "tcp",
    targetResourceId: "tool0",
    poseDelta: [0, 0, 0, 0, 0, 0]
  });
  return project;
}

export function runAbb120Suite(programs: Abb120SuiteProgram[], options: { now?: string } = {}): Abb120SuiteJob {
  const now = options.now ?? new Date().toISOString();
  const job_id = `A120-JOB-${now.replace(/[-:TZ.]/g, "").slice(0, 14)}-doc`;
  const compiled = programs.map((program) => {
    const ast = parseGrl(program.text);
    const ir = compileSemanticProgram(ast, {
      startJoints: [...ABB_IRB120_ZERO_JOINTS],
      sampleTime: 0.02
    });
    return { program, ast, ir };
  });
  const happyIr = compiled.find((item) => item.program.name.includes("A120_00"))?.ir ?? compiled[0]!.ir;
  const posted = postProcessAllBrands(happyIr);
  const abbRoundtrip = importBrandProgram("abb", [{ name: posted.outputs.abb.filename, text: posted.outputs.abb.text }], {
    projectName: "Abb120Roundtrip",
    generatedAt: now
  });
  const project = createAbb120OlpProject(programs[0]!);
  const report = createValidationReport({
    id: `A120-REPORT-${now.replace(/[-:TZ.]/g, "").slice(0, 14)}`,
    project,
    generatedAt: now,
    pathValidation: {
      ok: true,
      reachable: true,
      cycleTime: 1,
      segmentReports: [],
      diagnostics: []
    },
    postDiagnostics: posted.report.map((issue) => ({
      severity: issue.severity,
      code: issue.code,
      message: issue.message
    })),
    importDiagnostics: abbRoundtrip.report.sections.flatMap((section) => section.diagnostics)
  });
  const reportHtml = renderValidationReportHtml(report);
  const delivery = createCustomerDeliveryPackage({
    project,
    report,
    reportHtml,
    brandPrograms: Object.fromEntries(Object.values(posted.outputs).map((output) => [output.filename, output.text])),
    ioMap: { "io.do[1]": "gripper" },
    trace: { job_id, events: ["load", "run", "report"] }
  });

  return {
    job_id,
    robot: ABB120_ROBOT_FIXTURE,
    programs: compiled.map((item) => ({
      name: item.program.name,
      moduleName: item.ir.moduleName,
      diagnostics: item.ir.diagnostics,
      astKind: item.ast.kind,
      sourceMapEntries: item.ir.sourceMap.length,
      kdlMotionRequests: item.ir.kdlBridge.motionRequests.length,
      kdlPathRequests: item.ir.kdlBridge.pathRequests.length
    })),
    post: {
      filenames: Object.values(posted.outputs).map((output) => output.filename),
      report: posted.report.map((issue) => ({
        code: issue.code,
        severity: issue.severity,
        message: issue.message
      }))
    },
    roundtrip: {
      status: abbRoundtrip.report.status,
      diagnostics: abbRoundtrip.report.sections.flatMap((section) => section.diagnostics)
    },
    report_id: report.id,
    artifacts: {
      jobJson: `${job_id}/job.json`,
      traceJson: `${job_id}/trace.json`,
      trajectoryJson: `${job_id}/trajectory.json`,
      diagnosticsJson: `${job_id}/diagnostics.json`,
      reportJson: `${job_id}/reports/${report.id}.json`,
      reportHtml: `${job_id}/reports/${report.id}.html`,
      deliveryPackageJson: `${job_id}/delivery/package.json`,
      desktopScreenshot: `${job_id}/screenshots/virtual-controller-desktop.png`,
      mobileScreenshot: `${job_id}/screenshots/virtual-controller-mobile.png`
    }
  };
}
