import { TaxonomyDialog } from './TaxonomyDialog.js'
import type { DomainProperty } from '@api-client/core/modeling/DomainProperty.js'
import { SemanticType } from '@api-client/core/modeling/Semantics.js'
import { bound } from '../../decorators/bound.js'
import { html, nothing, TemplateResult } from 'lit'
import { renderHelp } from '../../help/render.js'
import {
  type GeospatialCoordinatesConfig,
  DEFAULT_GEOSPATIAL_CONFIG,
  GeospatialCoordinateFormat,
  GeospatialDistanceUnit,
  GeospatialSpatialReferenceSystem,
} from '@api-client/core/modeling/definitions/GeospatialCoordinates.js'

/**
 * A class that generates a dialog that allows the user to configure the taxonomy of a Geospatial Coordinates field.
 */
export class GeospatialCoordinatesTaxonomy extends TaxonomyDialog<GeospatialCoordinatesConfig> {
  static override title = `Configure Geospatial Coordinates Taxonomy`

  constructor(public domainElement: DomainProperty) {
    let config: GeospatialCoordinatesConfig
    const semantic = domainElement.semantics.find((s) => s.id === SemanticType.GeospatialCoordinates)
    if (semantic && semantic.config) {
      config = structuredClone(semantic.config) as GeospatialCoordinatesConfig
    } else {
      config = {}
    }
    super(config)
  }

  protected override formCallback(event: Event): Promise<void> {
    const semantic = this.domainElement.semantics.find((s) => s.id === SemanticType.GeospatialCoordinates)
    if (semantic) {
      semantic.config = this.config
    } else {
      this.domainElement.semantics.push({
        id: SemanticType.GeospatialCoordinates,
        config: this.config,
      })
    }
    this.domainElement.domain.notifyChange()
    return super.formCallback(event)
  }

  @bound
  handleValidationRuleChange(event: Event): void {
    const input = event.target as HTMLInputElement
    const value = input.value.trim()
    const name = input.name
    const bounds = (this.config?.validationBounds ?? {}) as Record<string, number>
    if (value) {
      // Update the config with the new validation rule
      const numeric = parseFloat(value)
      bounds[name] = numeric
    } else {
      // If the input is empty, remove the validation rule
      // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
      delete bounds[name]
    }
    this.config.validationBounds = bounds
    this.requestUpdate()
  }

  @bound
  render(): TemplateResult {
    return html`
      <div class="input-group">
        ${this.renderFormatSelect()} ${this.renderSpatialReferenceSystemSelect()} ${this.renderCoordinatePrecision()}
      </div>
      <div class="checkbox-group">
        ${this.renderEnableSpatialIndexing()} ${this.renderIncludeElevation()} ${this.renderEnableValidation()}
      </div>
      ${this.renderValidationSettings()}
      <div class="checkbox-group">
        ${this.renderEnableDistanceQueries()} ${this.renderEnableBoundingBoxQueries()}
        ${this.renderEnablePolygonQueries()}
      </div>
      ${this.renderQuerySettings()}
    `
  }

  protected renderFormatSelect(): TemplateResult {
    const value = this.config?.format ?? DEFAULT_GEOSPATIAL_CONFIG.format
    return html`
      <div class="row">
        <ui-select
          label="Coordinate Format"
          class="input"
          name="format"
          .value="${value as string}"
          @change="${this.handleSelectChange}"
        >
          <ui-option value="${GeospatialCoordinateFormat.LatLon}">
            <div>Latitude, Longitude</div>
            <div slot="supporting-text">Example: "40.7128,-74.0060."</div>
          </ui-option>
          <ui-option value="${GeospatialCoordinateFormat.LonLat}">
            <div>Longitude, Latitude</div>
            <div slot="supporting-text">Example: "-74.0060,40.7128."</div>
          </ui-option>
          <ui-option value="${GeospatialCoordinateFormat.PostGIS}">
            <div>PostGIS</div>
            <div slot="supporting-text">Example: "POINT(-74.0060 40.7128)"</div>
          </ui-option>
          <ui-option value="${GeospatialCoordinateFormat.Degree}">
            <div>Degree</div>
            <div slot="supporting-text">Example: "40.7128° N, 74.0060° W"</div>
          </ui-option>
          <ui-option value="${GeospatialCoordinateFormat.WKT}">
            <div>Well-Known Text (WKT)</div>
            <div slot="supporting-text">Example: "POINT(40.7128 -74.0060)"</div>
          </ui-option>
          <ui-option value="${GeospatialCoordinateFormat.GeoJSON}">
            <div>GeoJSON</div>
            <div slot="supporting-text">
              Example: <code>{"type": "Point", "coordinates": [-74.0060, 40.7128]}</code>
            </div>
          </ui-option>
        </ui-select>
        ${renderHelp('taxonomy', 'geospatial:format')}
      </div>
    `
  }

  protected renderSpatialReferenceSystemSelect(): TemplateResult {
    const format = this.config?.format ?? DEFAULT_GEOSPATIAL_CONFIG.format
    let value = this.config?.spatialReferenceSystem ?? DEFAULT_GEOSPATIAL_CONFIG.spatialReferenceSystem
    const isLocked = format === GeospatialCoordinateFormat.GeoJSON
    if (isLocked) {
      // If format is GeoJSON, use the default SRS
      value = GeospatialSpatialReferenceSystem.WGS84
    }
    let supportingText = ''
    if (format === GeospatialCoordinateFormat.GeoJSON) {
      supportingText = 'GeoJSON always uses WGS84 as the spatial reference system.'
    } else {
      supportingText = 'SRS is optional and we choose the most common ones.'
    }
    return html`
      <div class="row">
        <ui-select
          label="Spatial Reference System (SRS)"
          class="input"
          name="spatialReferenceSystem"
          .value="${value as string}"
          @change="${this.handleSelectChange}"
          ?disabled="${isLocked}"
          .supportingText="${supportingText}"
        >
          <ui-option value="${GeospatialSpatialReferenceSystem.WGS84}">
            <div>WGS84 (World Geodetic System 1984)</div>
            <div slot="supporting-text">Most common for GPS coordinates</div>
          </ui-option>
          <ui-option value="${GeospatialSpatialReferenceSystem.WebMercator}">
            <div>Web Mercator</div>
            <div slot="supporting-text">Common for web mapping</div>
          </ui-option>
          <ui-option value="${GeospatialSpatialReferenceSystem.NAD83}">
            <div>NAD83 (North American Datum 1983)</div>
            <div slot="supporting-text">Common in North America</div>
          </ui-option>
          <ui-option value="${GeospatialSpatialReferenceSystem.NAD27}">
            <div>NAD27 (North American Datum 1927)</div>
            <div slot="supporting-text">Common in older GPS systems</div>
          </ui-option>
        </ui-select>
        ${renderHelp('taxonomy', 'geospatial:spatialReferenceSystem')}
      </div>
    `
  }

  protected renderEnableSpatialIndexing(): TemplateResult {
    const enabled = this.config?.enableSpatialIndexing ?? DEFAULT_GEOSPATIAL_CONFIG.enableSpatialIndexing
    return html`
      <div class="row">
        <div class="input checkbox metadata">
          <ui-checkbox
            id="enable-spatial-indexing"
            .checked="${enabled}"
            name="enableSpatialIndexing"
            @change="${this.handleBooleanChange}"
          ></ui-checkbox>
          <div class="label">
            <label for="enable-spatial-indexing" class="body-medium">Enable Spatial Indexing</label>
            <span class="body-small meta">Enables automatic spatial indexing.</span>
          </div>
        </div>
        ${renderHelp('taxonomy', 'geospatial:enableSpatialIndexing')}
      </div>
    `
  }

  protected renderIncludeElevation(): TemplateResult {
    const enabled = this.config?.includeElevation ?? DEFAULT_GEOSPATIAL_CONFIG.includeElevation
    return html`
      <div class="row">
        <div class="input checkbox metadata">
          <ui-checkbox
            id="include-elevation"
            .checked="${enabled}"
            name="includeElevation"
            @change="${this.handleBooleanChange}"
          ></ui-checkbox>
          <div class="label">
            <label for="include-elevation" class="body-medium">Include Elevation</label>
            <span class="body-small meta">Includes elevation data in coordinates (3D coordinates).</span>
          </div>
        </div>
        ${renderHelp('taxonomy', 'geospatial:includeElevation')}
      </div>
    `
  }

  protected renderEnableValidation(): TemplateResult {
    const enabled = this.config?.enableValidation ?? DEFAULT_GEOSPATIAL_CONFIG.enableValidation
    return html`
      <div class="row">
        <div class="input checkbox metadata">
          <ui-checkbox
            id="enable-validation"
            .checked="${enabled}"
            name="enableValidation"
            @change="${this.handleBooleanChange}"
          ></ui-checkbox>
          <div class="label">
            <label for="enable-validation" class="body-medium">Enable Validation</label>
            <span class="body-small meta">Enables validation of geospatial coordinates.</span>
          </div>
        </div>
        ${renderHelp('taxonomy', 'geospatial:enableValidation')}
      </div>
    `
  }

  protected renderValidationSettings(): TemplateResult | typeof nothing {
    const hasValidation = this.config?.enableValidation ?? DEFAULT_GEOSPATIAL_CONFIG.enableValidation
    if (!hasValidation) {
      return nothing // Skip rendering validation settings if validation is not enabled
    }
    return html`<div class="input-group">
      ${this.renderMinLatitudeValidationInput()}${this.renderMaxLatitudeValidationInput()}
      ${this.renderMinLongitudeValidationInput()}${this.renderMaxLongitudeValidationInput()}
    </div>`
  }

  protected renderMinLatitudeValidationInput(): TemplateResult {
    const value = this.config?.minLatitude ?? DEFAULT_GEOSPATIAL_CONFIG.minLatitude
    return html`
      <div class="row input-row">
        <ui-outlined-text-field
          label="Minimum Latitude"
          class="input"
          name="minLatitude"
          type="number"
          min="-90"
          max="90"
          .value="${value as string}"
          @change="${this.handleValidationRuleChange}"
        ></ui-outlined-text-field>
        ${renderHelp('taxonomy', 'geospatial:minLatitude')}
      </div>
    `
  }

  protected renderMaxLatitudeValidationInput(): TemplateResult {
    const value = this.config?.maxLatitude ?? DEFAULT_GEOSPATIAL_CONFIG.maxLatitude
    return html`
      <div class="row input-row">
        <ui-outlined-text-field
          label="Maximum Latitude"
          class="input"
          name="maxLatitude"
          type="number"
          min="-90"
          max="90"
          .value="${value as string}"
          @change="${this.handleValidationRuleChange}"
        ></ui-outlined-text-field>
        ${renderHelp('taxonomy', 'geospatial:maxLatitude')}
      </div>
    `
  }

  protected renderMaxLongitudeValidationInput(): TemplateResult {
    const value = this.config?.maxLongitude ?? DEFAULT_GEOSPATIAL_CONFIG.maxLongitude
    return html`
      <div class="row input-row">
        <ui-outlined-text-field
          label="Maximum Longitude"
          class="input"
          name="maxLongitude"
          type="number"
          min="-180"
          max="180"
          .value="${value as string}"
          @change="${this.handleValidationRuleChange}"
        ></ui-outlined-text-field>
        ${renderHelp('taxonomy', 'geospatial:maxLongitude')}
      </div>
    `
  }

  protected renderMinLongitudeValidationInput(): TemplateResult {
    const value = this.config?.minLongitude ?? DEFAULT_GEOSPATIAL_CONFIG.minLongitude
    return html`
      <div class="row input-row">
        <ui-outlined-text-field
          label="Minimum Longitude"
          class="input"
          name="minLongitude"
          type="number"
          min="-180"
          max="180"
          .value="${value as string}"
          @change="${this.handleValidationRuleChange}"
        ></ui-outlined-text-field>
        ${renderHelp('taxonomy', 'geospatial:minLongitude')}
      </div>
    `
  }

  protected renderEnableDistanceQueries(): TemplateResult {
    const enabled = this.config?.enableDistanceQueries ?? DEFAULT_GEOSPATIAL_CONFIG.enableDistanceQueries
    return html`
      <div class="row">
        <div class="input checkbox metadata">
          <ui-checkbox
            id="enable-distance-queries"
            .checked="${enabled}"
            name="enableDistanceQueries"
            @change="${this.handleBooleanChange}"
          ></ui-checkbox>
          <div class="label">
            <label for="enable-distance-queries" class="body-medium">Enable Distance Queries</label>
            <span class="body-small meta">Whether to enable distance-based query endpoints.</span>
          </div>
        </div>
        ${renderHelp('taxonomy', 'geospatial:enableDistanceQueries')}
      </div>
    `
  }

  protected renderEnableBoundingBoxQueries(): TemplateResult {
    const enabled = this.config?.enableBoundingBoxQueries ?? DEFAULT_GEOSPATIAL_CONFIG.enableBoundingBoxQueries
    return html`
      <div class="row">
        <div class="input checkbox metadata">
          <ui-checkbox
            id="enable-bounding-box-queries"
            .checked="${enabled}"
            name="enableBoundingBoxQueries"
            @change="${this.handleBooleanChange}"
          ></ui-checkbox>
          <div class="label">
            <label for="enable-bounding-box-queries" class="body-medium">Enable Bounding Box Queries</label>
            <span class="body-small meta">Whether to enable bounding box query endpoints.</span>
          </div>
        </div>
        ${renderHelp('taxonomy', 'geospatial:enableBoundingBoxQueries')}
      </div>
    `
  }

  protected renderEnablePolygonQueries(): TemplateResult {
    const enabled = this.config?.enablePolygonQueries ?? DEFAULT_GEOSPATIAL_CONFIG.enablePolygonQueries
    return html`
      <div class="row">
        <div class="input checkbox metadata">
          <ui-checkbox
            id="enable-polygon-queries"
            .checked="${enabled}"
            name="enablePolygonQueries"
            @change="${this.handleBooleanChange}"
          ></ui-checkbox>
          <div class="label">
            <label for="enable-polygon-queries" class="body-medium">Enable Polygon Queries</label>
            <span class="body-small meta">Whether to enable polygon intersection query endpoints.</span>
          </div>
        </div>
        ${renderHelp('taxonomy', 'geospatial:enablePolygonQueries')}
      </div>
    `
  }

  protected renderQuerySettings(): TemplateResult | typeof nothing {
    const hasDistanceQueries = this.config?.enableDistanceQueries ?? DEFAULT_GEOSPATIAL_CONFIG.enableDistanceQueries
    const hasBoundingBoxQueries =
      this.config?.enableBoundingBoxQueries ?? DEFAULT_GEOSPATIAL_CONFIG.enableBoundingBoxQueries
    const hasPolygonQueries = this.config?.enablePolygonQueries ?? DEFAULT_GEOSPATIAL_CONFIG.enablePolygonQueries

    if (!hasDistanceQueries && !hasBoundingBoxQueries && !hasPolygonQueries) {
      return nothing // Skip rendering query settings if no queries are enabled
    }

    return html`<div class="input-group">
      ${this.renderDefaultDistanceUnitSelect()} ${this.renderMaxQueryDistanceInput()} ${this.renderApiEndpointPrefix()}
    </div>`
  }

  protected renderDefaultDistanceUnitSelect(): TemplateResult | typeof nothing {
    const hasDistanceQueries = this.config?.enableDistanceQueries ?? DEFAULT_GEOSPATIAL_CONFIG.enableDistanceQueries
    if (!hasDistanceQueries) {
      return nothing // Skip rendering if distance queries are not enabled
    }
    const value = this.config?.defaultDistanceUnit ?? DEFAULT_GEOSPATIAL_CONFIG.defaultDistanceUnit
    return html`
      <div class="row input-row">
        <ui-select
          label="Default Distance Unit"
          class="input"
          name="defaultDistanceUnit"
          .value="${value as string}"
          @change="${this.handleSelectChange}"
          supportingText="Used for distance calculations and spatial queries."
        >
          <ui-option value="${GeospatialDistanceUnit.Meters}">
            <div>Meters</div>
            <div slot="supporting-text">Standard unit for distance</div>
          </ui-option>
          <ui-option value="${GeospatialDistanceUnit.Kilometers}">
            <div>Kilometers</div>
            <div slot="supporting-text">Common for larger distances</div>
          </ui-option>
          <ui-option value="${GeospatialDistanceUnit.Miles}">
            <div>Miles</div>
            <div slot="supporting-text">Common in the US and UK</div>
          </ui-option>
          <ui-option value="${GeospatialDistanceUnit.Feet}">
            <div>Feet</div>
            <div slot="supporting-text">Common in the US and UK</div>
          </ui-option>
          <ui-option value="${GeospatialDistanceUnit.NauticalMiles}">
            <div>Nautical Miles</div>
            <div slot="supporting-text">Common for maritime and air navigation</div>
          </ui-option>
        </ui-select>
        ${renderHelp('taxonomy', 'geospatial:defaultDistanceUnit')}
      </div>
    `
  }

  protected renderMaxQueryDistanceInput(): TemplateResult | typeof nothing {
    const hasDistanceQueries = this.config?.enableDistanceQueries ?? DEFAULT_GEOSPATIAL_CONFIG.enableDistanceQueries
    if (!hasDistanceQueries) {
      return nothing // Skip rendering if distance queries are not enabled
    }
    const value = this.config?.maxQueryDistance ?? DEFAULT_GEOSPATIAL_CONFIG.maxQueryDistance
    return html`
      <div class="row input-row">
        <ui-outlined-text-field
          label="Maximum Query Distance"
          class="input"
          name="maxQueryDistance"
          type="number"
          min="0"
          .value="${typeof value === 'number' ? value.toString() : ''}"
          @change="${this.handleValidationRuleChange}"
          required
        ></ui-outlined-text-field>
        ${renderHelp('taxonomy', 'geospatial:maxQueryDistance')}
      </div>
    `
  }

  protected renderApiEndpointPrefix(): TemplateResult {
    // This is applicable to all geospatial coordinates queries
    const value = this.config?.apiEndpointPrefix ?? DEFAULT_GEOSPATIAL_CONFIG.apiEndpointPrefix
    return html`<div class="row input-row">
      <ui-outlined-text-field
        label="API Endpoint Prefix"
        class="input"
        name="apiEndpointPrefix"
        .value="${value as string}"
        @change="${this.handleTextChange}"
      ></ui-outlined-text-field>
      ${renderHelp('taxonomy', 'geospatial:apiEndpointPrefix')}
    </div>`
  }

  protected renderCoordinatePrecision(): TemplateResult {
    const value = this.config?.coordinatePrecision ?? DEFAULT_GEOSPATIAL_CONFIG.coordinatePrecision
    return html`
      <div class="row input-row">
        <ui-outlined-text-field
          label="Coordinate Precision"
          class="input"
          name="coordinatePrecision"
          type="number"
          min="0"
          max="10"
          .value="${typeof value === 'number' ? value.toString() : ''}"
          @change="${this.handleValidationRuleChange}"
          supportingText="Precision for coordinate storage (number of decimal places)"
        ></ui-outlined-text-field>
        ${renderHelp('taxonomy', 'geospatial:coordinatePrecision')}
      </div>
    `
  }
}
