/**
 * 측정 도구 핵심 로직
 * - MeasureSession: OL 객체 생명주기 관리
 * - 거리/면적/거리환 설정 함수
 * - 포맷 유틸리티
 *
 * 참조: mda-react-front/src/components/nav/rightNav/measure.ts
 */
import VectorSource from 'ol/source/Vector';
import VectorLayer from 'ol/layer/Vector';
import { Draw } from 'ol/interaction';
import { Overlay } from 'ol';
import { createBox } from 'ol/interaction/Draw';
import { unByKey } from 'ol/Observable';
import { getArea, getLength } from 'ol/sphere';
import { LineString } from 'ol/geom';

// =====================
// MeasureSession 클래스
// =====================

/**
 * 측정 세션: 생성한 OL 객체(레이어, 인터랙션, 오버레이, 리스너)를
 * 직접 추적하고, dispose() 한 번으로 일괄 정리.
 */
export class MeasureSession {
  constructor(map) {
    this.map = map;
    this._layer = null;
    this._interactions = [];
    this._overlays = [];
    this._listeners = [];
  }

  /** VectorLayer 생성+등록, source 반환 */
  createLayer() {
    const source = new VectorSource({ wrapX: false });
    this._layer = new VectorLayer({ source, zIndex: 54 });
    this.map.addLayer(this._layer);
    return source;
  }

  /** Draw 인터랙션 등록+추적 */
  addInteraction(draw) {
    this.map.addInteraction(draw);
    this._interactions.push(draw);
    return draw;
  }

  /** 측정 툴팁 Overlay 생성+등록+추적 */
  createTooltip() {
    const el = document.createElement('div');
    el.className = 'ol-tooltip ol-tooltip-measure';
    const overlay = new Overlay({
      element: el,
      offset: [0, -15],
      positioning: 'bottom-center',
    });
    this.map.addOverlay(overlay);
    this._overlays.push(overlay);
    return overlay;
  }

  /** 리스너 키 추적 (dispose 시 일괄 해제) */
  addListener(key) {
    if (key) this._listeners.push(key);
    return key;
  }

  /** 모든 추적 객체 일괄 제거 */
  dispose() {
    this._listeners.forEach((key) => unByKey(key));
    this._listeners = [];

    this._interactions.forEach((i) => this.map.removeInteraction(i));
    this._interactions = [];

    this._overlays.forEach((o) => this.map.removeOverlay(o));
    this._overlays = [];

    if (this._layer) {
      this.map.removeLayer(this._layer);
      this._layer = null;
    }
  }
}

// =====================
// 포맷 유틸리티
// =====================

/**
 * 거리 포맷: NM (km)
 * @param {number} meters
 * @returns {string} e.g. "5.2 NM (9.63 km)"
 */
export function formatDistance(meters) {
  const nm = ((meters / 1000) * 0.5399568035).toFixed(1);
  let sub;
  if (meters > 1000) {
    sub = (Math.round((meters / 1000) * 100) / 100) + ' km';
  } else {
    sub = (Math.round(meters * 100) / 100) + ' m';
  }
  return `${nm} NM (${sub})`;
}

/**
 * 면적 포맷: km² 또는 m²
 * @param {number} sqMeters
 * @returns {string}
 */
export function formatArea(sqMeters) {
  if (sqMeters > 10000) {
    return (Math.round((sqMeters / 1000000) * 100) / 100) + ' km\u00B2';
  }
  return (Math.round(sqMeters * 100) / 100) + ' m\u00B2';
}

/**
 * 각도 계산 (북쪽 기준 시계방향)
 * @param {number[]} start - [x, y] 맵 좌표
 * @param {number[]} end - [x, y] 맵 좌표
 * @param {number} [cog=0] - 선박 COG (도)
 * @returns {string} 각도 (0-360, 소수점 1자리)
 */
export function getCircleDegree(start, end, cog = 0) {
  const x = Number(end[0]) - Number(start[0]);
  const y = Number(end[1]) - Number(start[1]);

  const radian = Math.atan2(y, x) * (180 / Math.PI);
  let angle = 360 - (radian - 90);
  angle = (angle - cog) % 360;
  if (angle < 0) angle += 360;

  return angle.toFixed(1);
}

/**
 * 선분별 거리 툴팁 관리자
 * 좌표 배열이 변경될 때마다 선분 개수에 맞춰 툴팁을 생성/업데이트/제거
 */
class SegmentTooltips {
  constructor(session) {
    this.session = session;
    this.tooltips = []; // Overlay 배열
  }

  /**
   * 좌표 배열을 받아 각 선분 중점에 거리 툴팁 배치
   * @param {Array<number[]>} coords - 좌표 배열
   */
  update(coords) {
    const segCount = coords.length - 1;

    // 부족하면 툴팁 추가 생성
    while (this.tooltips.length < segCount) {
      const el = document.createElement('div');
      el.className = 'ol-tooltip ol-tooltip-segment';
      const overlay = new Overlay({
        element: el,
        offset: [0, -10],
        positioning: 'bottom-center',
      });
      this.session.map.addOverlay(overlay);
      this.session._overlays.push(overlay);
      this.tooltips.push(overlay);
    }

    // 남으면 숨기기
    for (let i = segCount; i < this.tooltips.length; i++) {
      this.tooltips[i].setPosition(undefined);
    }

    // 각 선분 거리 계산 및 표시
    for (let i = 0; i < segCount; i++) {
      const segLine = new LineString([coords[i], coords[i + 1]]);
      const length = getLength(segLine);
      const mid = [
        (coords[i][0] + coords[i + 1][0]) / 2,
        (coords[i][1] + coords[i + 1][1]) / 2,
      ];
      this.tooltips[i].getElement().innerHTML = formatDistance(length);
      this.tooltips[i].setPosition(mid);
    }
  }

  /** 모든 선분 툴팁을 static 스타일로 고정 */
  finalize() {
    this.tooltips.forEach((overlay) => {
      const el = overlay.getElement();
      if (el && overlay.getPosition()) {
        el.className = 'ol-tooltip ol-tooltip-segment-static';
      }
    });
  }
}

// =====================
// 도구 설정 함수
// =====================

/**
 * 거리 측정 설정 (LineString)
 * @param {MeasureSession} session
 * @param {VectorSource} source
 */
export function setupDistanceMeasure(session, source) {
  const draw = new Draw({ source, type: 'LineString' });
  session.addInteraction(draw);

  let currentTooltip = null;
  let segTooltips = null;

  draw.on('drawstart', (evt) => {
    const tooltip = session.createTooltip();
    currentTooltip = tooltip;
    segTooltips = new SegmentTooltips(session);
    const geom = evt.feature.getGeometry();

    const key = geom.on('change', (e) => {
      const coords = e.target.getCoordinates();
      const length = getLength(e.target);
      tooltip.getElement().innerHTML = formatDistance(length);
      tooltip.setPosition(e.target.getLastCoordinate());

      // 선분별 거리 표시 (2개 이상 좌표일 때)
      if (coords.length >= 2) {
        segTooltips.update(coords);
      }
    });
    session.addListener(key);
  });

  draw.on('drawend', () => {
    if (currentTooltip) {
      const el = currentTooltip.getElement();
      el.className = 'ol-tooltip ol-tooltip-static';
      currentTooltip.setOffset([0, -7]);
    }
    if (segTooltips) {
      segTooltips.finalize();
    }
  });
}

/**
 * 면적 측정 설정 (Polygon / Box / Circle)
 * @param {MeasureSession} session
 * @param {VectorSource} source
 * @param {'Polygon'|'Box'|'Circle'} shape
 */
export function setupAreaMeasure(session, source, shape) {
  // 메인 Draw 생성
  let draw;
  if (shape === 'Box') {
    draw = new Draw({ source, type: 'Circle', geometryFunction: createBox() });
  } else if (shape === 'Circle') {
    draw = new Draw({ source, type: 'Circle' });
  } else {
    draw = new Draw({ source, type: 'Polygon' });
  }
  session.addInteraction(draw);

  // Circle인 경우 반경 표시용 Line Draw 추가
  let lineDraw = null;
  let lineTooltip = null;
  if (shape === 'Circle') {
    lineTooltip = session.createTooltip();
    lineDraw = new Draw({ source, type: 'LineString' });
    session.addInteraction(lineDraw);

    lineDraw.on('drawstart', (evt) => {
      session.map.addOverlay(lineTooltip);
      const geom = evt.feature.getGeometry();

      const key = geom.on('change', (e) => {
        const length = getLength(e.target);
        const area = length * length * Math.PI;
        lineTooltip.getElement().innerHTML = formatArea(area);
        lineTooltip.setPosition(e.target.getFirstCoordinate());
      });
      session.addListener(key);
    });
  }

  let currentTooltip = null;
  let segTooltips = null;

  draw.on('drawstart', (evt) => {
    if (shape === 'Polygon' || shape === 'Box') {
      currentTooltip = session.createTooltip();
      segTooltips = new SegmentTooltips(session);
    }

    const geom = evt.feature.getGeometry();
    const key = geom.on('change', (e) => {
      if (shape === 'Polygon' || shape === 'Box') {
        const areaValue = getArea(e.target);
        currentTooltip.getElement().innerHTML = formatArea(areaValue);
        currentTooltip.setPosition(e.target.getInteriorPoint().getCoordinates());

        // 선분별 거리 표시
        const coords = e.target.getCoordinates()[0]; // 외부 링
        if (coords && coords.length >= 2) {
          segTooltips.update(coords);
        }
      }
    });
    session.addListener(key);
  });

  draw.on('drawend', () => {
    if (shape === 'Polygon' || shape === 'Box') {
      if (currentTooltip) {
        const el = currentTooltip.getElement();
        el.className = 'ol-tooltip ol-tooltip-static';
        currentTooltip.setOffset([0, -7]);
      }
      if (segTooltips) {
        segTooltips.finalize();
      }
    }
    if (shape === 'Circle' && lineDraw) {
      lineDraw.finishDrawing();
    }
  });
}

/**
 * 거리환 측정 설정 (Circle + Line 이중 Draw)
 * 참조: mda-react-front measure.ts getCircleMeasureInteraction
 *
 * @param {MeasureSession} session
 * @param {VectorSource} source
 */
export function setupRangeRingMeasure(session, source) {
  // Line Draw (반경 거리 표시)
  const lineTooltip = session.createTooltip();
  const lineDraw = new Draw({ source, type: 'LineString' });

  lineDraw.on('drawstart', (evt) => {
    session.map.addOverlay(lineTooltip);
    const geom = evt.feature.getGeometry();

    const key = geom.on('change', (e) => {
      const length = getLength(e.target);
      lineTooltip.getElement().innerHTML = formatDistance(length);
      lineTooltip.setPosition(e.target.getLastCoordinate());
    });
    session.addListener(key);
  });

  // Circle Draw (각도 표시)
  const circleDraw = new Draw({ source, type: 'Circle' });
  let circleTooltip = null;
  let degree = '0.0';

  circleDraw.on('drawstart', (evt) => {
    circleTooltip = session.createTooltip();

    const geom = evt.feature.getGeometry();
    const key = geom.on('change', () => {
      // sketchCoords_: [center, edge] — OL Draw 내부 좌표
      const coords = evt.target.sketchCoords_;
      if (coords && coords[0] && coords[1]) {
        degree = getCircleDegree(coords[0], coords[1]);
      }
      circleTooltip.getElement().innerHTML = `각도: ${degree}°`;
      circleTooltip.setPosition(geom.getCenter());
    });
    session.addListener(key);
  });

  circleDraw.on('drawend', () => {
    lineDraw.finishDrawing();

    if (circleTooltip) {
      const el = circleTooltip.getElement();
      el.className = 'ol-tooltip ol-tooltip-static';
      // 최종 툴팁: 거리 + 각도
      el.innerHTML = lineTooltip.getElement().innerHTML + ` 각도: ${degree}°`;
      circleTooltip.setOffset([0, -7]);
    }

    session.map.removeOverlay(lineTooltip);
  });

  // circle → line 순서로 인터랙션 등록 (OL 이벤트 처리 순서)
  session.addInteraction(circleDraw);
  session.addInteraction(lineDraw);
}
