export type RobotHandle = number;

export interface MotionSourceMap {
  file?: string;
  line?: number;
  column?: number;
  module?: string;
}

export interface MotionDiagnostic {
  severity: "info" | "warning" | "error";
  code: string;
  message: string;
  time?: number;
  pointIndex?: number;
  segmentId?: string;
  targetId?: string;
  sourceMap?: MotionSourceMap;
  data?: Record<string, unknown>;
}

export interface KdlError {
  code: string;
  message: string;
  diagnostics: MotionDiagnostic[];
}

export interface KdlInitOptions {
  wrapperUrl?: string;
  wasmUrl?: string;
  useThreads?: boolean;
  wasmBuild?: string;
}

export interface KdlRuntimeInfo {
  version: string;
  kdlVersion?: string;
  wasmBuild: string;
  supportsThreads: boolean;
  supportsWasmFs: boolean;
}

export interface Pose {
  position: [number, number, number];
  quaternion: [number, number, number, number];
}

export type PoseLike =
  | Pose
  | { xyz: [number, number, number]; rpy: [number, number, number] }
  | { xyz: [number, number, number]; quat: [number, number, number, number] };

export interface RobotConfiguration {
  shoulder?: -1 | 0 | 1;
  elbow?: -1 | 0 | 1;
  wrist?: -1 | 0 | 1;
  turnNumbers?: number[];
}

export interface PoseTarget {
  id?: string;
  pose: Pose;
  config?: RobotConfiguration;
  tool?: Pose;
  frame?: Pose;
  extAxis?: number[];
  sourceMap?: MotionSourceMap;
}

export interface JointTarget {
  id?: string;
  joints: number[];
  extAxis?: number[];
  sourceMap?: MotionSourceMap;
}

export type SpeedSpec =
  | { kind: "joint_percent"; value: number }
  | { kind: "joint_abs"; velocity: number; acceleration?: number }
  | { kind: "linear"; velocity: number; acceleration?: number; angularVelocity?: number };

export type ZoneSpec =
  | { kind: "fine" }
  | { kind: "distance"; value: number }
  | { kind: "cnt"; value: number }
  | { kind: "continuous" };

export interface JointLimits {
  name: string;
  lower: number;
  upper: number;
  velocity: number;
  acceleration: number;
  jerk?: number;
}

export interface RobotInfo {
  handle: RobotHandle;
  robotId: string;
  name: string;
  baseLink: string;
  tipLink: string;
  dof: number;
  jointNames: string[];
  limits: JointLimits[];
}

export type JointType = "revolute" | "continuous" | "prismatic" | "fixed";

export interface LinkModel {
  name: string;
}

export interface JointModel {
  name: string;
  type: JointType;
  parent: string;
  child: string;
  origin: {
    xyz: [number, number, number];
    rpy: [number, number, number];
  };
  axis: [number, number, number];
  limit?: JointLimits;
}

export interface NormalizedRobotModel {
  robotId: string;
  baseLink: string;
  tipLink: string;
  name: string;
  links: LinkModel[];
  joints: JointModel[];
  activeJointNames: string[];
  limits: JointLimits[];
  source: {
    type: "urdf";
    urdfHash: string;
  };
}

export type JsonObject = Record<string, unknown>;
export interface JointLimitOverride {
  name: string;
  lower?: number;
  upper?: number;
  velocity?: number;
  acceleration?: number;
  jerk?: number;
}

export interface UrdfLoadOptions {
  robotId: string;
  baseLink: string;
  tipLink: string;
  tool?: Pose;
  base?: Pose;
  jointOrder?: string[];
  overrideLimits?: JointLimitOverride[];
}
export type PoseNormalizeOptions = JsonObject;
export interface OffsetSpec {
  mode: "frame" | "tool" | "world";
  frameId?: string;
  xyz?: [number, number, number];
  rpy?: [number, number, number];
  quaternion?: [number, number, number, number];
}
export interface FkOptions {
  tool?: Pose;
  frame?: Pose;
  includeFlange?: boolean;
}

export interface FkResult {
  ok: boolean;
  flange: Pose;
  tcp: Pose;
  joints: number[];
  diagnostics: MotionDiagnostic[];
}

export type Pose7Array = Float64Array | [number, number, number, number, number, number, number] | number[];

export interface LinkPoseResult {
  ok: boolean;
  linkPoses: Array<{ link: string; pose: Pose }>;
  diagnostics: MotionDiagnostic[];
}
export type JacobianOptions = JsonObject;
export interface JacobianResult {
  ok: boolean;
  rows: number;
  cols: number;
  data: Float64Array | number[];
  diagnostics: MotionDiagnostic[];
}
export interface IkOptions {
  tool?: Pose;
  frame?: Pose;
  qMin?: number[];
  qMax?: number[];
  maxIterations?: number;
  positionTolerance?: number;
  orientationTolerance?: number;
  seeds?: number[][];
  preferredConfig?: RobotConfiguration;
  allowApproximate?: boolean;
}

export interface IkResult {
  ok: boolean;
  joints?: number[];
  iterations: number;
  residualPosition?: number;
  residualOrientation?: number;
  configuration?: RobotConfiguration;
  reason?: "unreachable" | "joint_limit" | "singularity" | "max_iteration" | "invalid_model";
  diagnostics: MotionDiagnostic[];
}
export interface LimitCheckResult {
  ok: boolean;
  diagnostics: MotionDiagnostic[];
  maxJointVelocityRatio?: number;
  maxJointAccelerationRatio?: number;
}

export interface SingularityResult {
  ok: boolean;
  nearSingularity: boolean;
  manipulability?: number;
  conditionNumber?: number;
  diagnostics: MotionDiagnostic[];
}

export interface ReachabilityResult {
  ok: boolean;
  reachable: boolean;
  targetId?: string;
  joints?: number[];
  residualPosition?: number;
  residualOrientation?: number;
  nearestPose?: Pose;
  diagnostics: MotionDiagnostic[];
}
export interface TrapProfileOptions {
  maxVelocity: number;
  maxAcceleration: number;
  sampleTime: number;
  startVelocity?: number;
  endVelocity?: number;
}

export interface TrapSample {
  index: number;
  time: number;
  s: number;
  sd: number;
  sdd: number;
}

export interface TrapProfileResult {
  ok: boolean;
  type: "trapezoid" | "triangle";
  length: number;
  duration: number;
  tAccel: number;
  tConst: number;
  tDecel: number;
  vPeak: number;
  samples: TrapSample[];
  diagnostics: MotionDiagnostic[];
}
export interface MoveJRequest {
  startJoints: number[];
  target: JointTarget | PoseTarget;
  speed: SpeedSpec;
  zone: ZoneSpec;
  tool?: Pose;
  frame?: Pose;
  sampleTime: number;
  speedOverride?: number;
  sourceMap?: MotionSourceMap;
}

export interface MoveLRequest {
  startJoints: number[];
  target: PoseTarget;
  speed: SpeedSpec;
  zone: ZoneSpec;
  tool?: Pose;
  frame?: Pose;
  sampleTime: number;
  orientationMode?: "fixed" | "slerp" | "tool_z_lock";
  ik?: IkOptions;
  speedOverride?: number;
  sourceMap?: MotionSourceMap;
}

export interface MoveCRequest {
  startJoints: number[];
  via: PoseTarget;
  target: PoseTarget;
  speed: SpeedSpec;
  zone: ZoneSpec;
  tool?: Pose;
  frame?: Pose;
  sampleTime: number;
  orientationMode?: "fixed" | "slerp";
  arcMode?: "via" | "center" | "radius";
  circleDirection?: "short" | "long" | "cw" | "ccw";
  ik?: IkOptions;
  speedOverride?: number;
  sourceMap?: MotionSourceMap;
}
export type MotionKind = "MOVEJ" | "MOVEL" | "MOVEC";

export interface TrajectoryEvent {
  id?: string;
  time: number;
  pointIndex: number;
  kind: string;
  sourceMap?: MotionSourceMap;
  data?: JsonObject;
}

export interface TrajectoryPoint {
  index: number;
  time: number;
  dt: number;
  s: number;
  sd: number;
  sdd: number;
  joints: number[];
  jointVelocity: number[];
  jointAcceleration: number[];
  flange: Pose;
  tcp: Pose;
  tcpVelocity?: [number, number, number, number, number, number];
  tcpAcceleration?: [number, number, number, number, number, number];
  motion: MotionKind;
  segmentId?: string;
  targetId?: string;
  sourceMap?: MotionSourceMap;
  diagnostics: MotionDiagnostic[];
}

export interface TrajectoryResult {
  ok: boolean;
  motion: MotionKind;
  duration: number;
  sampleTime: number;
  points: TrajectoryPoint[];
  events: TrajectoryEvent[];
  diagnostics: MotionDiagnostic[];
  meta?: JsonObject;
}
export interface MotionSegmentRequest {
  id: string;
  motion: MotionKind;
  target?: JointTarget | PoseTarget;
  via?: PoseTarget;
  targetId?: string;
  speed: SpeedSpec;
  zone: ZoneSpec;
  tool?: Pose;
  frame?: Pose;
  sourceMap?: MotionSourceMap;
  source?: JsonObject;
}

export interface PathEventRequest {
  id?: string;
  timing: "before" | "after" | "at";
  pointId: string;
  distance?: number;
  kind: string;
  sourceMap?: MotionSourceMap;
  data?: JsonObject;
}

export interface PathPlanRequest {
  pathId?: string;
  startJoints: number[];
  segments: MotionSegmentRequest[];
  events?: PathEventRequest[];
  sampleTime: number;
  speedOverride?: number;
  stopOnError?: boolean;
  source?: JsonObject;
}

export interface PathPlanResult {
  ok: boolean;
  duration: number;
  segments: TrajectoryResult[];
  points: TrajectoryPoint[];
  diagnostics: MotionDiagnostic[];
}

export interface SegmentValidationReport {
  segmentId: string;
  ok: boolean;
  motion: MotionKind;
  duration?: number;
  maxJointVelocityRatio?: number;
  maxJointAccelerationRatio?: number;
  maxCartesianError?: number;
  diagnostics: MotionDiagnostic[];
}

export interface PathValidationResult {
  ok: boolean;
  reachable: boolean;
  cycleTime?: number;
  segmentReports: SegmentValidationReport[];
  diagnostics: MotionDiagnostic[];
}
export interface CycleTimeSegment {
  segmentId?: string;
  motion: MotionKind;
  duration: number;
}

export interface CycleTimeResult {
  ok: boolean;
  motionTime: number;
  waitTime?: number;
  ioTime?: number;
  totalTime: number;
  segmentTimes: CycleTimeSegment[];
  diagnostics: MotionDiagnostic[];
}

export interface PerformanceMetric {
  name: string;
  iterations: number;
  totalMs: number;
  averageMs: number;
  thresholdMs?: number;
  maxMs?: number;
  points?: number;
  ok: boolean;
}

export interface PerformanceBaselineResult {
  ok: boolean;
  metrics: PerformanceMetric[];
  diagnostics: MotionDiagnostic[];
}

export interface KdlWasmApi {
  init(options?: KdlInitOptions): Promise<KdlRuntimeInfo>;
  dispose(): Promise<void>;

  loadRobotFromUrdf(urdfXml: string, options: UrdfLoadOptions): Promise<RobotHandle>;
  createRobotFromModel(model: NormalizedRobotModel): Promise<RobotHandle>;
  destroyRobot(handle: RobotHandle): Promise<void>;
  getRobotInfo(handle: RobotHandle): Promise<RobotInfo>;
  getJointLimits(handle: RobotHandle): Promise<JointLimits[]>;

  normalizePose(input: PoseLike, options?: PoseNormalizeOptions): Promise<Pose>;
  composePose(a: Pose, b: Pose): Promise<Pose>;
  inversePose(pose: Pose): Promise<Pose>;
  applyToolAndFrame(target: PoseTarget, tool: Pose, frame: Pose): Promise<Pose>;
  applyOffset(target: PoseTarget, offset: OffsetSpec): Promise<PoseTarget>;

  fk(handle: RobotHandle, joints: Float64Array, options?: FkOptions): Promise<FkResult>;
  fkPose7(handle: RobotHandle, joints: Float64Array, out?: Float64Array, options?: FkOptions): Promise<Float64Array>;
  fkAllLinks(handle: RobotHandle, joints: Float64Array, options?: FkOptions): Promise<LinkPoseResult>;
  jacobian(handle: RobotHandle, joints: Float64Array, options?: JacobianOptions): Promise<JacobianResult>;
  ik(handle: RobotHandle, seed: Float64Array, target: Pose, options?: IkOptions): Promise<IkResult>;
  ikBatch(handle: RobotHandle, seeds: Float64Array[], targets: Pose[], options?: IkOptions): Promise<IkResult[]>;

  checkJointLimits(handle: RobotHandle, joints: Float64Array): Promise<LimitCheckResult>;
  checkVelocityLimits(handle: RobotHandle, trajectory: TrajectoryResult): Promise<LimitCheckResult>;
  checkSingularity(handle: RobotHandle, joints: Float64Array): Promise<SingularityResult>;
  checkReachability(handle: RobotHandle, target: PoseTarget, options?: IkOptions): Promise<ReachabilityResult>;
  checkReachabilityBatch(handle: RobotHandle, targets: PoseTarget[], options?: IkOptions): Promise<ReachabilityResult[]>;

  makeTrapProfile(length: number, options: TrapProfileOptions): Promise<TrapProfileResult>;
  sampleTrapProfile(length: number, options: TrapProfileOptions): Promise<TrapSample[]>;

  planMoveJ(handle: RobotHandle, request: MoveJRequest): Promise<TrajectoryResult>;
  planMoveL(handle: RobotHandle, request: MoveLRequest): Promise<TrajectoryResult>;
  planMoveC(handle: RobotHandle, request: MoveCRequest): Promise<TrajectoryResult>;

  planPath(handle: RobotHandle, request: PathPlanRequest): Promise<PathPlanResult>;
  validatePath(handle: RobotHandle, request: PathPlanRequest): Promise<PathValidationResult>;
  estimateCycleTime(input: TrajectoryResult | PathPlanResult): Promise<CycleTimeResult>;
  resampleTrajectory(trajectory: TrajectoryResult, sampleTime: number): Promise<TrajectoryResult>;
}

export type KdlApiMethod = keyof KdlWasmApi;
