// ** Resource Types **

import { Position, LineString, MultiLineString } from './resources/geojson';
import { FBCharts, FBRoute } from './resources/freeboard';
import {
  SKMeteo,
  SKSaR,
  SKAtoN,
  SKAircraft,
  SKVessel
} from '../modules/skresources/resource-classes';
import { DEPTH_UNIT } from '../lib/convert';
import { Options } from '../modules/map/ol/lib/charts/s57.service';
import { ChartImageAdjustment } from './resources/signalk';

export * from './resources/signalk';
export * from './resources/custom';
export * from './resources/geojson';
export * from './resources/freeboard';
export * from './stream';

export type ErrorList = Array<{ status: number; message: string }>;

export type MFBAction = 'wpt' | 'pob' | 'autopilot' | 'radar';

// Glyph used to render wind direction/speed on the weather overlays and meteo
// targets. Shared by the display config, the settings option map, and the layer
// component inputs.
export type WindIndicator = 'arrow' | 'barb';

// What panning the chart does while follow-vessel mode is on.
// 'offset' holds the panned-to look-ahead until follow is turned off,
// 'exit' releases follow mode, 'none' keeps the vessel centred (the map
// returns to it on the next position update).
export type MapPanBehavior = 'offset' | 'exit' | 'none';

// Screen-fixed offset of the map centre from the vessel: each axis is a whole
// percentage of the distance from the viewport centre to that edge. `x` is
// across the screen (positive = right), `y` up it (positive = above the vessel,
// i.e. look ahead). Fixed to the screen, so it does not rotate with the course.
export interface MapCenterOffset {
  x: number;
  y: number;
}

export interface SKApiResponse {
  state: 'FAILED' | 'COMPLETED' | 'PENDING';
  statusCode: number;
  message: string;
  requestId?: string;
  href?: string;
  token?: string;
}

export interface CourseData {
  dtg: number;
  ttg: number;
  eta: Date;
  route: {
    dtg: number;
    ttg: number;
    eta: Date;
  };
  vmg: number;
  bearing: { value: number; type: string };
  bearingTrue: number;
  bearingMagnetic: number;
  xte: number;
  position: Position;
  pointIndex: number;
  pointTotal: number;
  arrivalCircle: number;
  startPosition: Position;
  pointNames: string[];
  activeRoutePoints: LineString;
  destPointName: string;
}

export type TemperatureUnitDef = 'C' | 'F';
export type DepthUnitDef = 'm' | 'foot';
export type LengthUnitDef = 'm' | 'foot';
export type SpeedUnitDef = 'kn' | 'm/s' | 'km/h' | 'mph';
export type DistanceUnitDef = 'kilometer' | 'naut-mile';

export type PalettePosition = { x: number; y: number };

export interface IAppConfig {
  ui: {
    mapNorthUp: boolean;
    mapMove: boolean; // keep vessel centered
    mapConstrainZoom: boolean; // constrain zooming to map min / max values
    toolbarButtons: boolean; // show/hide toolbar buttons
    invertColor: boolean; // invert map feature label colors
    showCourseData: boolean; // show/hide course data
    showAisTargets: boolean; // show/hide AIS targets
    showNotes: boolean; // show/hide notes
    autoNightMode: boolean;
  };
  imageAdjustPalettePos: PalettePosition | null; // remembered drag offset of the chart Image Adjustment palette
  display: {
    fab: MFBAction; // FAB button selection
    disableWakelock: boolean;
    darkMode: { enabled: boolean; source: 0 | 1 | -1 }; // source: 0= browser default, 1= Signal K mode, -1=manual)
    nightMode: boolean; // auto set night mode based on environment.mode
    muteSound: boolean;
    depthAlarm: { enabled: boolean; smoothing: number };
    plugins: {
      instruments: string;
      startOnOpen: boolean;
      parameters: string | null;
      favourites: string[];
    };
    preferInfoPanel: boolean;
    singleClickNoteDetails: boolean;
    windIndicator: WindIndicator; // glyph used to render wind direction/speed
    statusBar: {
      liveEta: boolean; // show cursor bearing/distance/ETA in the status bar
      tapFadeSpeed: number; // seconds a tapped-point marker takes to fade (and the readout stays held on it)
      referenceSpeed: number; // fallback boat speed (in display speed units) for ETA when SOG is ~0
    };
  };
  units: {
    distance: DistanceUnitDef;
    depth: DepthUnitDef;
    length: LengthUnitDef;
    speed: SpeedUnitDef;
    temperature: TemperatureUnitDef;
    positionFormat: 'XY' | 'SHDd' | 'HDd' | 'DMdH' | 'HDMS' | 'DHMS';
    headingAttribute: 'navigation.headingTrue' | 'navigation.headingMagnetic';
    preferredPaths: {
      tws: string;
      twd: string;
      heading: string;
      course: string;
    };
    useServerPrefs: boolean;
  };
  map: {
    // ** map config
    zoomLevel: number;
    center: Position;
    rotation: number;
    panBehavior: MapPanBehavior; // what panning the chart does while following the vessel
    animate: boolean;
    labelsMinZoom: number;
    doubleClickZoom: boolean; // true=zoom
    overZoomTiles: boolean; // keep tiles visible beyond chart max zoom
    centerOffset: MapCenterOffset; // screen-fixed map-centre offset from the vessel
    s57Options: Options; // S57 chart Options
    popoverMulti: boolean; // close popovers using cose button
  };
  course: {
    autoNextPointOnArrival: boolean;
    autoNextPointDelay: number;
    autoNextPointTrigger: 'perpendicularPassed' | 'arrivalCircleEntered';
    arrivalCircle: number;
  };
  vessels: {
    fixedLocationMode: boolean;
    fixedPosition: Position;
    trail: boolean; // display trail
    windVectors: boolean; // display vessel TWD, AWD vectors
    laylines: boolean;
    selfLines: {
      cog: {
        length: number; // (minutes) length = cogLine * sog
        color: string;
        weight: number;
        dash: LineStyleDash;
      };
      heading: {
        length: number; // mode for display of heading line -1 = default
        color: string;
        weight: number;
        dash: LineStyleDash;
      };
    };
    aisCogLine: number; // (minutes) length = cogLine * sog
    iconScale: number; // scale to apply to self Vessel icon
    rangeCircles: boolean; //display range circles
    rangeCirclesFixed: boolean; // use a fixed distance rather than zoom level calc
    rangeCirclesDistance: number; // distance between circles when fixed is true
    rangeCircleCount: number; // number of circles to display
    rangeCircleMinZoom: number; // min zoom level where they are displayed
    aisStaleAge: number; // time since last update in ms (6 min)
    aisMaxAge: number; // time since last update in ms (9 min)
    aisWindApparent: boolean;
    aisWindMinZoom: number;
    aisShowTrack: boolean;
    trailFromServer: boolean;
    trailDuration: number; // number of hours of trail to fetch from server
    trailResolution: {
      // resolution of server trail at defined time horizons
      lastHour: string;
      next23: string;
      beyond24: string;
    };
  };
  resources: {
    // ** resource options
    fetchFilter: string; // param string to provide record filtering
    fetchRadius: number; // radius (nmi/km) within which to return resources
    notes: {
      rootFilter: string; // param string to provide record filtering
      getRadius: number; // radius (nmi/km) within which to return notes
      groupNameEdit: boolean;
      groupRequiresPosition: boolean;
      minZoom: number;
    };
    video: {
      enable: boolean;
      url: string | null;
    };
    paths: string[];
  };
  signalk: {
    // signal k connection options
    vessels: boolean;
    atons: boolean;
    aircraft: boolean;
    sar: boolean;
    meteo: boolean;
    maxRadius: number; // max radius within which AIS targets are displayed
    proxied: boolean; // server behind a proxy server
  };
  radars: {
    deviceId: string;
    opacity: number;
  };
  experiments: boolean;
  plotterExtensions: {
    // ** plotter extension host (plotterExtensions resource type).
    // No enabled list: extension availability is controlled on the server
    // (plugin install/enable); presence in the resources response = enabled.
    widgets: Array<{
      instanceId: string; // host-assigned GUID for this placement
      extension: string; // extension (resource) id
      widget: string; // manifest-local widget id
      anchor: 'tr' | 'ct' | 'cb' | 'bl' | 'br';
      col: number; // origin cell column (0 | 1)
      row: number; // origin cell row (0 | 1)
    }>;
  };
  anchor: {
    radius: number; // most recent anchor radius setting
    setRadius: boolean; // checks inital anchor radius setting
    manualSet: boolean; // checks manual set setting
    rodeLength: number; // rode length setting
  };
  selections: {
    // ** saved selections
    routes: string[];
    waypoints: string[];
    regions: string[];
    tracks: string[] | null;
    charts: string[];
    chartOrder: string[]; // chart layer ordering
    chartOpacity: Record<string, number>;
    chartImageAdjustment: Record<string, ChartImageAdjustment>;
    chartDisplayMinZoom: Record<string, number>;
    aisTargets: string[];
    aisTargetTypes: number[];
    aisFilterByShipType: boolean;
    aisState: string[]; // list of ais state values used to filter targets
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    resourceSets: { [key: string]: any }; // additional resources
    infolayers: string[];
    weatherWindEnabled: boolean;
    oceanCurrentsEnabled: boolean;
    tidalCurrentsEnabled: boolean;
  };
}

export interface FBAppData {
  loginRequired: boolean;
  chartBounds: {
    show: boolean;
    charts: FBCharts;
  };
  selfId: string;
  activeRoute: string;
  activeRouteReversed: boolean;
  activeRouteCircular: boolean;
  activeRouteIsEditing: boolean;
  editingId: string;
  activeWaypoint: string;
  serverTrail: boolean; // trail received from server
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  server: { [key: string]: any }; // SK server info
  lastGet: Position; // map position of last resources GET
  map: {
    atClick: {
      // eslint-disable-next-line @typescript-eslint/no-explicit-any
      features: any[];
      lonlat: Position;
    };
  };
  vessels: {
    // received vessel data
    showSelf: boolean;
    self: SKVessel;
    aisTargets: Map<string, SKVessel>;
    aisTracks: Map<string, MultiLineString>; // AIS targets track (tracks plugin)
    activeId: string;
    active: SKVessel;
    closest: string[];
    prefAvailablePaths: { [key: string]: string }; // preference paths available from source
    flagged: string[];
    showTrack: string[]; // ais targets to display track for (session-only, independent of aisShowTrack)
  };
  aircraft: Map<string, SKAircraft>; // received AIS aircraft data
  atons: Map<string, SKAtoN>; // received AIS AtoN data
  sar: Map<string, SKSaR>; // received AIS SaR data
  meteo: Map<string, SKMeteo>; // received AIS Meteo data
  racing: {
    startLine: LineString;
  };
}

export interface SKServerUnitPrefs {
  name: string;
  categories: Record<string, SKUnitCategory | undefined>;
}

export type SKUnitCategory = {
  baseUnit: string;
  targetUnit: string;
  displayFormat: string;
  symbol: string;
};

/**
 * Per-path display-unit override as published by the Signal K server in a path's
 * metadata (`meta.displayUnits`). Carries the user's preferred unit for that
 * specific path, which can differ from the category preset (e.g. wind speed in m/s
 * while boat speed is in knots).
 *
 * `formatValueForDisplay()` currently consumes `targetUnit` and `symbol`. The
 * `formula`/`inverseFormula` (Math.js) and `displayFormat` fields are part of the
 * server contract but not yet consumed — reserved for future use.
 */
export type SKPathDisplayUnits = {
  category: string;
  targetUnit: string;
  formula?: string;
  inverseFormula?: string;
  symbol?: string;
  displayFormat?: string;
};
