import type { AsukaCoordinateSpace, AsukaCoordinateUnit } from './renderEngine';

export type AsukaPhysicalUnit = 'm' | 'mm' | 'cm' | 'in';

export interface AsukaPhysicalSizeValue {
  width: number;
  height: number;
  unit?: AsukaPhysicalUnit;
}

/** Physical calibration attached to a view or one print/drawing area. */
export interface AsukaPhysicalSize extends AsukaPhysicalSizeValue {
  unit: AsukaPhysicalUnit;
  /** Optional quality threshold used for raster-image diagnostics. */
  recommendedDpi?: number;
  /** Optional size-specific calibrations, keyed by a business variation id such as S, M or L. */
  variants?: Record<string, AsukaPhysicalSizeValue>;
  /** Variation used when the caller does not provide one explicitly. */
  activeVariantId?: string;
}

export interface AsukaNativeLayerMeasurement {
  width: number;
  height: number;
  referenceWidth?: number;
  referenceHeight?: number;
  space: AsukaCoordinateSpace;
  unit: AsukaCoordinateUnit;
}

export interface AsukaRelativeLayerMeasurement {
  width?: number;
  height?: number;
  area?: number;
  unit: 'percent';
}

export interface AsukaPhysicalLayerMeasurement {
  calibrated: boolean;
  width?: number;
  height?: number;
  area?: number;
  unit?: AsukaPhysicalUnit;
  areaUnit?: 'm2' | 'mm2' | 'cm2' | 'in2';
  referenceWidth?: number;
  referenceHeight?: number;
  source?: 'print-area' | 'view' | 'model' | 'explicit';
  variationId?: string;
}

export interface AsukaImageResolutionMeasurement {
  pixelWidth?: number;
  pixelHeight?: number;
  effectiveDpiX?: number;
  effectiveDpiY?: number;
  minimumDpi: number;
  sufficient: boolean | null;
  vector: boolean;
  warning?: 'SOURCE_SIZE_UNKNOWN' | 'RESOLUTION_TOO_LOW';
}

export interface AsukaLayerMeasurement {
  objectId: string;
  native: AsukaNativeLayerMeasurement;
  relative: AsukaRelativeLayerMeasurement;
  physical: AsukaPhysicalLayerMeasurement;
  image?: AsukaImageResolutionMeasurement;
  constraints: {
    fits: boolean | null;
    maxWidth?: number;
    maxHeight?: number;
    unit?: AsukaPhysicalUnit;
  };
}

export interface AsukaProductionLayerMeasurement {
  id: string;
  kind?: string;
  label?: string;
  printAreaId?: string | null;
  measurement: AsukaLayerMeasurement;
}

export interface AsukaProductionMeasurementExport {
  schema: 'asuka-production-measurements@1';
  view: {
    id: string;
    engine: string;
    physicalSize?: AsukaPhysicalSize;
  };
  printAreas: Array<{
    id: string;
    label?: string;
    physicalSize?: AsukaPhysicalSize;
  }>;
  layers: AsukaProductionLayerMeasurement[];
}

export interface AsukaLayerMeasurementOptions {
  /** Fallback used when the object's print area has no physical calibration. */
  physicalSize?: AsukaPhysicalSize | null;
  physicalSource?: AsukaPhysicalLayerMeasurement['source'];
  targetUnit?: AsukaPhysicalUnit;
  variationId?: string | null;
  minimumDpi?: number;
}

export interface AsukaPhysicalLayerResizeInput extends AsukaLayerMeasurementOptions {
  width?: number;
  height?: number;
  unit: AsukaPhysicalUnit;
  /** Defaults to true, preserving the layer aspect ratio. */
  preserveAspectRatio?: boolean;
}

export interface CreateAsukaLayerMeasurementInput extends AsukaLayerMeasurementOptions {
  objectId: string;
  native: AsukaNativeLayerMeasurement;
  sourcePixelWidth?: number;
  sourcePixelHeight?: number;
  vector?: boolean;
}

const INCHES_PER_UNIT: Record<AsukaPhysicalUnit, number> = {
  in: 1,
  m: 1 / 0.0254,
  cm: 1 / 2.54,
  mm: 1 / 25.4,
};

const AREA_UNITS: Record<AsukaPhysicalUnit, 'm2' | 'mm2' | 'cm2' | 'in2'> = {
  m: 'm2',
  mm: 'mm2',
  cm: 'cm2',
  in: 'in2',
};

function finitePositive(value: unknown): number | null {
  const number = Number(value);
  return Number.isFinite(number) && number > 0 ? number : null;
}

export function convertPhysicalLength(value: number, from: AsukaPhysicalUnit, to: AsukaPhysicalUnit): number {
  if (from === to) return value;
  return (value * INCHES_PER_UNIT[from]) / INCHES_PER_UNIT[to];
}

export function normalizePhysicalSize(input: unknown, variationId?: string | null): (AsukaPhysicalSizeValue & {
  unit: AsukaPhysicalUnit;
  recommendedDpi?: number;
  variationId?: string;
}) | null {
  if (!input || typeof input !== 'object') return null;
  const raw = input as AsukaPhysicalSize;
  const baseUnit: AsukaPhysicalUnit = raw.unit === 'm' || raw.unit === 'mm' || raw.unit === 'in' ? raw.unit : 'cm';
  const requestedVariation = String(variationId ?? raw.activeVariantId ?? '').trim();
  const variants = raw.variants && typeof raw.variants === 'object' ? raw.variants : null;
  const variantKey = requestedVariation && variants
    ? Object.keys(variants).find((key) => key === requestedVariation || key.toLowerCase() === requestedVariation.toLowerCase())
    : undefined;
  const variant = variantKey ? variants?.[variantKey] : null;
  const width = finitePositive(variant?.width ?? raw.width);
  const height = finitePositive(variant?.height ?? raw.height);
  if (width == null || height == null) return null;
  const unit = variant?.unit === 'm' || variant?.unit === 'mm' || variant?.unit === 'cm' || variant?.unit === 'in'
    ? variant.unit
    : baseUnit;
  const recommendedDpi = finitePositive(raw.recommendedDpi) ?? undefined;
  return {
    width,
    height,
    unit,
    recommendedDpi,
    ...(variantKey ? { variationId: variantKey } : null),
  };
}

export function createAsukaLayerMeasurement(input: CreateAsukaLayerMeasurementInput): AsukaLayerMeasurement {
  const nativeWidth = Math.max(0, Number(input.native.width) || 0);
  const nativeHeight = Math.max(0, Number(input.native.height) || 0);
  const referenceWidth = finitePositive(input.native.referenceWidth);
  const referenceHeight = finitePositive(input.native.referenceHeight);
  const widthRatio = referenceWidth ? nativeWidth / referenceWidth : null;
  const heightRatio = referenceHeight ? nativeHeight / referenceHeight : null;
  const relative: AsukaRelativeLayerMeasurement = {
    unit: 'percent',
    ...(widthRatio != null ? { width: widthRatio * 100 } : null),
    ...(heightRatio != null ? { height: heightRatio * 100 } : null),
    ...(widthRatio != null && heightRatio != null ? { area: widthRatio * heightRatio * 100 } : null),
  };

  const calibration = normalizePhysicalSize(input.physicalSize, input.variationId);
  const targetUnit = input.targetUnit ?? calibration?.unit;
  const canCalibrate = Boolean(calibration && targetUnit && widthRatio != null && heightRatio != null);
  const calibratedReferenceWidth = calibration && targetUnit
    ? convertPhysicalLength(calibration.width, calibration.unit, targetUnit)
    : undefined;
  const calibratedReferenceHeight = calibration && targetUnit
    ? convertPhysicalLength(calibration.height, calibration.unit, targetUnit)
    : undefined;
  const physicalWidth = canCalibrate ? Math.max(0, widthRatio! * calibratedReferenceWidth!) : undefined;
  const physicalHeight = canCalibrate ? Math.max(0, heightRatio! * calibratedReferenceHeight!) : undefined;
  const physical: AsukaPhysicalLayerMeasurement = canCalibrate
    ? {
        calibrated: true,
        width: physicalWidth,
        height: physicalHeight,
        area: physicalWidth! * physicalHeight!,
        unit: targetUnit,
        areaUnit: AREA_UNITS[targetUnit!],
        referenceWidth: calibratedReferenceWidth,
        referenceHeight: calibratedReferenceHeight,
        source: input.physicalSource ?? 'explicit',
        ...(calibration?.variationId ? { variationId: calibration.variationId } : null),
      }
    : { calibrated: false };

  const epsilon = 1e-7;
  const fits = canCalibrate
    ? physicalWidth! <= calibratedReferenceWidth! + epsilon && physicalHeight! <= calibratedReferenceHeight! + epsilon
    : null;

  const vector = input.vector === true;
  const pixelWidth = finitePositive(input.sourcePixelWidth) ?? undefined;
  const pixelHeight = finitePositive(input.sourcePixelHeight) ?? undefined;
  const minimumDpi = finitePositive(input.minimumDpi) ?? calibration?.recommendedDpi ?? 150;
  let image: AsukaImageResolutionMeasurement | undefined;
  if (vector || pixelWidth || pixelHeight) {
    const widthInches = physicalWidth && targetUnit ? physicalWidth * INCHES_PER_UNIT[targetUnit] : null;
    const heightInches = physicalHeight && targetUnit ? physicalHeight * INCHES_PER_UNIT[targetUnit] : null;
    const effectiveDpiX = pixelWidth && widthInches ? pixelWidth / widthInches : undefined;
    const effectiveDpiY = pixelHeight && heightInches ? pixelHeight / heightInches : undefined;
    const sufficient = vector
      ? true
      : effectiveDpiX && effectiveDpiY
        ? Math.min(effectiveDpiX, effectiveDpiY) >= minimumDpi
        : null;
    image = {
      pixelWidth,
      pixelHeight,
      effectiveDpiX,
      effectiveDpiY,
      minimumDpi,
      sufficient,
      vector,
      ...(sufficient === false ? { warning: 'RESOLUTION_TOO_LOW' as const } : null),
      ...(!vector && sufficient == null ? { warning: 'SOURCE_SIZE_UNKNOWN' as const } : null),
    };
  }

  return {
    objectId: String(input.objectId ?? ''),
    native: {
      ...input.native,
      width: nativeWidth,
      height: nativeHeight,
      ...(referenceWidth ? { referenceWidth } : null),
      ...(referenceHeight ? { referenceHeight } : null),
    },
    relative,
    physical,
    ...(image ? { image } : null),
    constraints: {
      fits,
      ...(canCalibrate ? { maxWidth: calibratedReferenceWidth, maxHeight: calibratedReferenceHeight, unit: targetUnit } : null),
    },
  };
}
