import type { Feature, FeatureCollection, Point } from "geojson";
import type { ExpressionSpecification, Map as MapLibreMap, StyleSpecification } from "maplibre-gl";
import { MAP_STYLE_TOKENS } from "./map-colors";

export const SCADA_ANALYSIS_SOURCE_ID = "agent-scada-analysis";
export const SCADA_ANALYSIS_LAYER_IDS = {
  casing: "agent-scada-analysis-casing",
  level: "agent-scada-analysis-level",
  indicator: "agent-scada-analysis-indicator",
  label: "agent-scada-analysis-label"
} as const;

export const SCADA_ANALYSIS_LEVELS = ["high", "medium", "low", "unrated"] as const;
export type ScadaAnalysisLevel = (typeof SCADA_ANALYSIS_LEVELS)[number];
export type ScadaAnalysisItem = { sensor_id: string; level: ScadaAnalysisLevel };
export type ScadaAnalysisLevelCounts = Record<ScadaAnalysisLevel, number>;
export type ScadaAnalysisRenderResult = {
  rendered_ids: string[];
  missing_ids: string[];
  level_counts: ScadaAnalysisLevelCounts;
  fitted: true;
};

export const SCADA_ANALYSIS_LEVEL_COLORS: Record<ScadaAnalysisLevel, string> = {
  high: MAP_STYLE_TOKENS.state.incident,
  medium: MAP_STYLE_TOKENS.state.risk,
  low: MAP_STYLE_TOKENS.state.success,
  unrated: MAP_STYLE_TOKENS.state.agent
};

const SCADA_ANALYSIS_LABEL_COLORS: Record<ScadaAnalysisLevel, string> = {
  high: MAP_STYLE_TOKENS.stateLabel.danger,
  medium: MAP_STYLE_TOKENS.stateLabel.warning,
  low: MAP_STYLE_TOKENS.stateLabel.success,
  unrated: MAP_STYLE_TOKENS.stateLabel.agent
};

const levelColorExpression = (
  colors: Record<ScadaAnalysisLevel, string>
): ExpressionSpecification => [
  "match",
  ["get", "analysis_level"],
  "high", colors.high,
  "medium", colors.medium,
  "low", colors.low,
  colors.unrated
];

export const SCADA_ANALYSIS_LEVEL_LABELS: Record<ScadaAnalysisLevel, string> = {
  high: "高",
  medium: "中",
  low: "低",
  unrated: "未分级"
};

const SCADA_ANALYSIS_SORT_PRIORITY: Record<ScadaAnalysisLevel, number> = {
  high: 0,
  medium: 1,
  low: 2,
  unrated: 3
};

type Layer = NonNullable<StyleSpecification["layers"]>[number];

export const scadaAnalysisLayers: Layer[] = [
  {
    id: SCADA_ANALYSIS_LAYER_IDS.casing,
    type: "circle",
    source: SCADA_ANALYSIS_SOURCE_ID,
    paint: {
      "circle-radius": 20,
      "circle-color": "rgba(255,255,255,0)",
      "circle-stroke-color": "#FFFFFF",
      "circle-stroke-width": 8
    }
  },
  {
    id: SCADA_ANALYSIS_LAYER_IDS.level,
    type: "circle",
    source: SCADA_ANALYSIS_SOURCE_ID,
    paint: {
      "circle-radius": 20,
      "circle-color": "rgba(255,255,255,0)",
      "circle-stroke-color": levelColorExpression(SCADA_ANALYSIS_LEVEL_COLORS),
      "circle-stroke-width": 5
    }
  },
  {
    id: SCADA_ANALYSIS_LAYER_IDS.indicator,
    type: "circle",
    source: SCADA_ANALYSIS_SOURCE_ID,
    paint: {
      "circle-radius": 6,
      "circle-translate": [14, -14],
      "circle-color": levelColorExpression(SCADA_ANALYSIS_LEVEL_COLORS),
      "circle-stroke-color": "#FFFFFF",
      "circle-stroke-width": 2
    }
  },
  {
    id: SCADA_ANALYSIS_LAYER_IDS.label,
    type: "symbol",
    source: SCADA_ANALYSIS_SOURCE_ID,
    layout: {
      "symbol-sort-key": ["get", "sort_priority"],
      "text-field": ["get", "analysis_label"],
      "text-size": 18,
      "text-font": ["Noto Sans Regular"],
      "text-letter-spacing": 0.015,
      "text-offset": [0, -2.2],
      "text-anchor": "bottom",
      "text-allow-overlap": false,
      "text-ignore-placement": false
    },
    paint: {
      "text-color": levelColorExpression(SCADA_ANALYSIS_LABEL_COLORS),
      "text-halo-color": "#FFFFFF",
      "text-halo-width": 4,
      "text-halo-blur": 0.5
    }
  }
];

export function ensureScadaAnalysisLayers(
  map: Pick<MapLibreMap, "addLayer" | "getLayer">
): void {
  scadaAnalysisLayers.forEach((layer, index) => {
    if (map.getLayer(layer.id)) return;
    const beforeId = scadaAnalysisLayers
      .slice(index + 1)
      .map((candidate) => candidate.id)
      .find((candidateId) => Boolean(map.getLayer(candidateId)));
    map.addLayer(layer, beforeId);
  });
}

export function createEmptyScadaAnalysisCollection(): FeatureCollection<Point> {
  return { type: "FeatureCollection", features: [] };
}

export function parseScadaAnalysisItems(value: unknown): ScadaAnalysisItem[] | null {
  if (!Array.isArray(value) || value.length < 1 || value.length > 100) return null;
  const items: ScadaAnalysisItem[] = [];
  const seen = new Set<string>();
  for (const entry of value) {
    if (!entry || typeof entry !== "object" || Array.isArray(entry)) return null;
    const keys = Object.keys(entry);
    if (keys.length !== 2 || !keys.includes("sensor_id") || !keys.includes("level")) return null;
    const { sensor_id: rawSensorId, level } = entry as Record<string, unknown>;
    const sensorId = typeof rawSensorId === "string" ? rawSensorId.trim() : "";
    if (
      !sensorId ||
      sensorId.length > 128 ||
      !SCADA_ANALYSIS_LEVELS.includes(level as ScadaAnalysisLevel) ||
      seen.has(sensorId)
    ) return null;
    seen.add(sensorId);
    items.push({ sensor_id: sensorId, level: level as ScadaAnalysisLevel });
  }
  return items;
}

export function createScadaAnalysisCollection(
  collection: FeatureCollection,
  items: ScadaAnalysisItem[]
): { collection: FeatureCollection<Point>; result: Omit<ScadaAnalysisRenderResult, "fitted"> } {
  const featuresBySensorId = new Map<string, Feature<Point>>();
  collection.features.forEach((feature) => {
    const sensorId = typeof feature.properties?.sensor_id === "string"
      ? feature.properties.sensor_id.trim()
      : "";
    if (sensorId && feature.geometry?.type === "Point" && !featuresBySensorId.has(sensorId)) {
      featuresBySensorId.set(sensorId, feature as Feature<Point>);
    }
  });

  const levelCounts = createEmptyScadaAnalysisLevelCounts();
  const renderedIds: string[] = [];
  const missingIds: string[] = [];
  const features: Array<Feature<Point>> = [];
  items.forEach((item) => {
    const feature = featuresBySensorId.get(item.sensor_id);
    if (!feature) {
      missingIds.push(item.sensor_id);
      return;
    }
    const trustedSensorId = String(feature.properties?.sensor_id ?? "").trim();
    if (!trustedSensorId) {
      missingIds.push(item.sensor_id);
      return;
    }
    renderedIds.push(trustedSensorId);
    levelCounts[item.level] += 1;
    features.push({
      ...feature,
      properties: {
        ...feature.properties,
        sensor_id: trustedSensorId,
        analysis_level: item.level,
        analysis_level_label: SCADA_ANALYSIS_LEVEL_LABELS[item.level],
        analysis_label: `${trustedSensorId} · ${SCADA_ANALYSIS_LEVEL_LABELS[item.level]}`,
        sort_priority: SCADA_ANALYSIS_SORT_PRIORITY[item.level]
      }
    });
  });

  return {
    collection: { type: "FeatureCollection", features },
    result: { rendered_ids: renderedIds, missing_ids: missingIds, level_counts: levelCounts }
  };
}

export function createEmptyScadaAnalysisLevelCounts(): ScadaAnalysisLevelCounts {
  return { high: 0, medium: 0, low: 0, unrated: 0 };
}
