/**
 * MapTools - Drawing and Measurement Tools
 * 
 * Provides:
 * - Circle measurement tool with radius/area tooltip
 * - Control bar with drawing tools
 * - Line and polygon measurement
 * 
 * Refactored from olmapstuffgis.js for LUPMIS PWA
 */

import { Draw } from 'ol/interaction';
import { Circle as CircleStyle, Fill, Stroke, Style } from 'ol/style';
import { Vector as VectorLayer } from 'ol/layer';
import { Vector as VectorSource } from 'ol/source';
import Overlay from 'ol/Overlay';
import { LineString, Circle, Polygon } from 'ol/geom';
import { getLength, getArea } from 'ol/sphere';
import Feature from 'ol/Feature';
import { unByKey } from 'ol/Observable';
import { formatLength, formatArea, formatCircleExtent } from '../units.js';

// ol-ext imports
import EditBar from 'ol-ext/control/EditBar';
import Toggle from 'ol-ext/control/Toggle';
import Button from 'ol-ext/control/Button';

export class MapTools {
  constructor(map, options = {}) {
    this.map = map;
    this.options = options;
    
    // Create measurement layer
    this.measureSource = new VectorSource();
    this.measureLayer = new VectorLayer({
      source: this.measureSource,
      style: this.getMeasureStyle(),
      title: 'Measurements',
      zIndex: 100,
    });

    // Create drawing layer (utility layer for temporary draw interactions;
    // hidden from LayerSwitcher since the EditBar has its own "Drawings" group)
    this.drawSource = new VectorSource();
    this.drawLayer = new VectorLayer({
      source: this.drawSource,
      style: this.getDrawStyle(),
      title: 'Draw sketches',
      displayInLayerSwitcher: false,
      zIndex: 99,
    });

    // Insert both layers just before the Overlays group so the LayerSwitcher
    // order becomes: Overlays > Measurements > Markers > Base Maps. Locate the
    // Overlays group by title rather than assuming it is the last layer —
    // other layers (e.g. the GPS trail/position layers) may sit on top of it.
    const layers = this.map.getLayers();
    let overlayIdx = layers.getArray().findIndex((l) => l.get('title') === 'Overlays');
    if (overlayIdx < 0) overlayIdx = layers.getLength();
    layers.insertAt(overlayIdx, this.drawLayer);
    layers.insertAt(overlayIdx, this.measureLayer);
    
    // Active interaction
    this.activeInteraction = null;
    this.measureTooltip = null;
    this.measureTooltipElement = null;
    
    // Callbacks
    this.onMeasureCompleteCallbacks = [];
    this.onDrawCompleteCallbacks = [];
  }
  
  /**
   * Get style for measurement features
   */
  getMeasureStyle() {
    return new Style({
      fill: new Fill({
        color: 'rgba(255, 233, 106, 0.2)'
      }),
      stroke: new Stroke({
        color: '#8B008B',
        lineDash: [10, 10],
        width: 2
      }),
      image: new CircleStyle({
        radius: 5,
        stroke: new Stroke({
          color: '#8B008B'
        }),
        fill: new Fill({
          color: 'rgba(255, 233, 106, 0.5)'
        })
      })
    });
  }
  
  /**
   * Get style for drawing features
   */
  getDrawStyle() {
    return new Style({
      fill: new Fill({
        color: 'rgba(255, 233, 106, 0.3)'
      }),
      stroke: new Stroke({
        color: '#8B008B',
        width: 2
      }),
      image: new CircleStyle({
        radius: 6,
        stroke: new Stroke({
          color: '#8B008B',
          width: 2
        }),
        fill: new Fill({
          color: '#FFE96A'
        })
      })
    });
  }
  
  /**
   * Create measurement tooltip overlay
   */
  createMeasureTooltip() {
    if (this.measureTooltipElement) {
      this.measureTooltipElement.parentNode.removeChild(this.measureTooltipElement);
    }
    this.measureTooltipElement = document.createElement('div');
    this.measureTooltipElement.className = 'measure-tooltip';
    this.measureTooltip = new Overlay({
      element: this.measureTooltipElement,
      offset: [15, 0],
      positioning: 'center-left',
      stopEvent: false,
    });
    this.map.addOverlay(this.measureTooltip);
  }
  
  /**
   * Remove any active interaction
   */
  deactivate() {
    if (this.activeInteraction) {
      this.map.removeInteraction(this.activeInteraction);
      this.activeInteraction = null;
    }
    if (this.measureTooltip) {
      this.map.removeOverlay(this.measureTooltip);
      this.measureTooltip = null;
    }
    if (this.measureTooltipElement && this.measureTooltipElement.parentNode) {
      this.measureTooltipElement.parentNode.removeChild(this.measureTooltipElement);
      this.measureTooltipElement = null;
    }
  }
  
  /**
   * Start circle measurement tool
   * Draws a circle and shows radius + area in tooltip
   */
  startCircleMeasure() {
    this.deactivate();
    this.createMeasureTooltip();
    
    const drawCircle = new Draw({
      source: this.measureSource,
      type: 'Circle',
      style: new Style({
        fill: new Fill({
          color: 'rgba(255, 233, 106, 0.2)'
        }),
        stroke: new Stroke({
          color: 'rgba(139, 0, 139, 0.7)',
          lineDash: [10, 10],
          width: 2
        }),
        image: new CircleStyle({
          radius: 5,
          stroke: new Stroke({
            color: 'rgba(139, 0, 139, 0.7)'
          }),
          fill: new Fill({
            color: 'rgba(255, 233, 106, 0.5)'
          })
        })
      })
    });
    
    this.activeInteraction = drawCircle;
    this.map.addInteraction(drawCircle);
    
    let listener;
    
    drawCircle.on('drawstart', (evt) => {
      const sketch = evt.feature;
      
      listener = sketch.getGeometry().on('change', (e) => {
        const geom = e.target;
        
        if (geom instanceof Circle) {
          const radius = geom.getRadius();
          const area = formatCircleExtent(radius);
          const radiusFormatted = formatLength(radius);
          
          const output = `<strong>${radiusFormatted}</strong><br><small>${area}</small>`;
          
          this.measureTooltipElement.innerHTML = output;
          this.measureTooltip.setPosition(geom.getLastCoordinate());
        }
      });
    });
    
    drawCircle.on('drawend', (evt) => {
      const feature = evt.feature;
      const geom = feature.getGeometry();
      const center = geom.getCenter();
      const radius = geom.getRadius();

      // Tag the circle feature so the dblclick handler can identify it
      feature.set('_layerType', 'measure_circle');
      feature.set('_radius', radius);
      feature.set('_center', center);

      // Create radius line for visualization
      const radiusLine = new Feature({
        geometry: new LineString([
          center,
          [center[0] + radius, center[1]]
        ])
      });
      radiusLine.set('_layerType', 'measure_circle_radius');
      this.measureSource.addFeature(radiusLine);
      
      // Make tooltip static
      this.measureTooltipElement.className = 'measure-tooltip measure-tooltip-static';
      this.measureTooltip.setOffset([0, -7]);
      
      // Create new tooltip for next measurement
      this.measureTooltipElement = null;
      this.createMeasureTooltip();
      
      unByKey(listener);
      
      // Trigger callbacks
      const result = {
        type: 'circle',
        center: center,
        radius: radius,
        area: Math.PI * radius * radius,
        feature: feature,
      };
      this.onMeasureCompleteCallbacks.forEach(cb => cb(result));
    });
    
    return drawCircle;
  }
  
  /**
   * Start line measurement tool
   */
  startLineMeasure() {
    this.deactivate();
    this.createMeasureTooltip();
    
    const drawLine = new Draw({
      source: this.measureSource,
      type: 'LineString',
      style: this.getMeasureStyle(),
    });
    
    this.activeInteraction = drawLine;
    this.map.addInteraction(drawLine);
    
    let listener;
    
    drawLine.on('drawstart', (evt) => {
      const sketch = evt.feature;
      
      listener = sketch.getGeometry().on('change', (e) => {
        const geom = e.target;
        const length = getLength(geom);
        const output = formatLength(length);
        
        this.measureTooltipElement.innerHTML = output;
        this.measureTooltip.setPosition(geom.getLastCoordinate());
      });
    });
    
    drawLine.on('drawend', (evt) => {
      const feature = evt.feature;
      const geom = feature.getGeometry();
      const length = getLength(geom);
      
      this.measureTooltipElement.className = 'measure-tooltip measure-tooltip-static';
      this.measureTooltipElement = null;
      this.createMeasureTooltip();
      
      unByKey(listener);
      
      const result = {
        type: 'line',
        length: length,
        feature: feature,
      };
      this.onMeasureCompleteCallbacks.forEach(cb => cb(result));
    });
    
    return drawLine;
  }
  
  /**
   * Start polygon/area measurement tool
   */
  startAreaMeasure() {
    this.deactivate();
    this.createMeasureTooltip();
    
    const drawPolygon = new Draw({
      source: this.measureSource,
      type: 'Polygon',
      style: this.getMeasureStyle(),
    });
    
    this.activeInteraction = drawPolygon;
    this.map.addInteraction(drawPolygon);
    
    let listener;
    
    drawPolygon.on('drawstart', (evt) => {
      const sketch = evt.feature;
      
      listener = sketch.getGeometry().on('change', (e) => {
        const geom = e.target;
        const area = getArea(geom);
        const output = formatArea(area);
        
        this.measureTooltipElement.innerHTML = output;
        this.measureTooltip.setPosition(geom.getInteriorPoint().getCoordinates());
      });
    });
    
    drawPolygon.on('drawend', (evt) => {
      const feature = evt.feature;
      const geom = feature.getGeometry();
      const area = getArea(geom);

      // Tag so the double-click handler can identify it
      feature.set('_layerType', 'measure_area');
      feature.set('_area', area);

      this.measureTooltipElement.className = 'measure-tooltip measure-tooltip-static';
      this.measureTooltipElement = null;
      this.createMeasureTooltip();

      unByKey(listener);

      const result = {
        type: 'polygon',
        area: area,
        feature: feature,
        coordinate: geom.getInteriorPoint().getCoordinates(),
      };
      this.onMeasureCompleteCallbacks.forEach(cb => cb(result));
    });
    
    return drawPolygon;
  }
  
  /**
   * Start point drawing tool
   */
  startDrawPoint() {
    this.deactivate();
    
    const drawPoint = new Draw({
      source: this.drawSource,
      type: 'Point',
      style: this.getDrawStyle(),
    });
    
    this.activeInteraction = drawPoint;
    this.map.addInteraction(drawPoint);
    
    drawPoint.on('drawend', (evt) => {
      const result = {
        type: 'point',
        feature: evt.feature,
      };
      this.onDrawCompleteCallbacks.forEach(cb => cb(result));
    });
    
    return drawPoint;
  }
  
  /**
   * Start line drawing tool
   */
  startDrawLine() {
    this.deactivate();
    
    const drawLine = new Draw({
      source: this.drawSource,
      type: 'LineString',
      style: this.getDrawStyle(),
    });
    
    this.activeInteraction = drawLine;
    this.map.addInteraction(drawLine);
    
    drawLine.on('drawend', (evt) => {
      const result = {
        type: 'line',
        feature: evt.feature,
      };
      this.onDrawCompleteCallbacks.forEach(cb => cb(result));
    });
    
    return drawLine;
  }
  
  /**
   * Start polygon drawing tool
   */
  startDrawPolygon() {
    this.deactivate();
    
    const drawPolygon = new Draw({
      source: this.drawSource,
      type: 'Polygon',
      style: this.getDrawStyle(),
    });
    
    this.activeInteraction = drawPolygon;
    this.map.addInteraction(drawPolygon);
    
    drawPolygon.on('drawend', (evt) => {
      const result = {
        type: 'polygon',
        feature: evt.feature,
      };
      this.onDrawCompleteCallbacks.forEach(cb => cb(result));
    });
    
    return drawPolygon;
  }
  
  /**
   * Clear all measurements
   */
  clearMeasurements() {
    this.measureSource.clear();
    // Remove static tooltips
    const tooltips = document.querySelectorAll('.measure-tooltip-static');
    tooltips.forEach(el => el.parentNode.removeChild(el));
  }
  
  /**
   * Clear all drawings
   */
  clearDrawings() {
    this.drawSource.clear();
  }
  
  /**
   * Clear all (measurements + drawings)
   */
  clearAll() {
    this.clearMeasurements();
    this.clearDrawings();
  }
  
  /**
   * Register callback for measurement completion
   */
  onMeasureComplete(callback) {
    this.onMeasureCompleteCallbacks.push(callback);
  }
  
  /**
   * Register callback for drawing completion
   */
  onDrawComplete(callback) {
    this.onDrawCompleteCallbacks.push(callback);
  }
  
  /**
   * Create a control bar with measurement and drawing tools
   * Returns the ol-ext Bar control
   */
  createControlBar(options = {}) {
    const position = options.position || 'top-left';
    
    // Main control bar
    const mainBar = new EditBar({
      group: true,
      className: 'map-tools-bar',
    });
    
    // Measurement toggle group
    const measureBar = new EditBar({
      toggleOne: true,
      group: true,
    });
    
    // Circle measure button
    const circleBtn = new Toggle({
      html: '<span class="tool-icon">⭕</span>',
      title: 'Measure Circle (radius & area)',
      className: 'measure-circle-btn',
      onToggle: (active) => {
        if (active) {
          this.startCircleMeasure();
        } else {
          this.deactivate();
        }
      }
    });
    measureBar.addControl(circleBtn);
    
    // Line measure button
    const lineBtn = new Toggle({
      html: '<span class="tool-icon">📏</span>',
      title: 'Measure Distance',
      className: 'measure-line-btn',
      onToggle: (active) => {
        if (active) {
          this.startLineMeasure();
        } else {
          this.deactivate();
        }
      }
    });
    measureBar.addControl(lineBtn);
    
    // Area measure button
    const areaBtn = new Toggle({
      html: '<span class="tool-icon">⬛</span>',
      title: 'Measure Area',
      className: 'measure-area-btn',
      onToggle: (active) => {
        if (active) {
          this.startAreaMeasure();
        } else {
          this.deactivate();
        }
      }
    });
    measureBar.addControl(areaBtn);
    
    // Clear measurements button
    const clearBtn = new Button({
      html: '<span class="tool-icon">🗑️</span>',
      title: 'Clear Measurements',
      className: 'clear-measure-btn',
      handleClick: () => {
        this.clearMeasurements();
        // Deactivate any active toggle
        circleBtn.setActive(false);
        lineBtn.setActive(false);
        areaBtn.setActive(false);
      }
    });
    measureBar.addControl(clearBtn);
    
    mainBar.addControl(measureBar);
    
    return mainBar;
  }
  
  /**
   * Get the measure layer
   */
  getMeasureLayer() {
    return this.measureLayer;
  }
  
  /**
   * Get the draw layer
   */
  getDrawLayer() {
    return this.drawLayer;
  }
  
  /**
   * Get the measure source
   */
  getMeasureSource() {
    return this.measureSource;
  }
  
  /**
   * Get the draw source
   */
  getDrawSource() {
    return this.drawSource;
  }
  
  /**
   * Check if any tool is currently active
   */
  isActive() {
    return this.activeInteraction !== null;
  }
}

export default MapTools;
