import type { Property, PropertyUnit, UnitBundle, UnitNeedMatch } from '../domain/property'
import { mockLatentNeeds } from '../mock-data/latentNeeds'

// How many m² of tolerance above need.max we still consider a match (e.g. 20%)
const OVERSIZE_TOLERANCE = 0.25

function areaScore(unitSqm: number, min: number, max: number): number {
  const effectiveMax = max * (1 + OVERSIZE_TOLERANCE)
  if (unitSqm < min || unitSqm > effectiveMax) return 0
  // Perfect score when unit size is in the middle of the range
  const mid = (min + max) / 2
  const spread = max - min
  const dist = Math.abs(unitSqm - mid)
  return Math.max(0.5, 1 - dist / (spread + 1))
}

function assetTypeMatch(property: Property, needAssetType: string): number {
  if (property.assetType === needAssetType) return 1
  // Mixed-use partial credit
  if (String(property.assetType).toUpperCase() === 'MIXED') return 0.7
  return 0
}

/** Find needs that a single unit can satisfy (full or partial) */
export const unitMatchService = {
  getMatchesForUnit(
    unit: PropertyUnit,
    property: Property,
  ): UnitNeedMatch[] {
    const results: UnitNeedMatch[] = []

    for (const need of mockLatentNeeds) {
      const { min, max } = need.sizeRange
      const atMatch = assetTypeMatch(property, String(need.assetType))
      if (atMatch === 0) continue

      const offeredSqm = unit.offeredSqm ?? unit.areaSqm

      // Full unit fits the need
      const fullScore = areaScore(offeredSqm, min, max)
      if (fullScore > 0) {
        results.push({
          needId: need.id,
          tenantName: need.tenantCompany ?? need.title,
          tenantCompany: need.tenantCompany,
          requiredSqmMin: min,
          requiredSqmMax: max,
          matchScore: Math.round(fullScore * atMatch * 95),
          matchType: 'exact',
        })
        continue
      }

      // Partial: unit is larger than need.max but unit is flexible — offer a slice
      if (unit.isFlexible && offeredSqm > max && (unit.minLettableSqm ?? 0) <= max) {
        const partialSqm = Math.min(offeredSqm, max)
        const partialScore = areaScore(partialSqm, min, max)
        if (partialScore > 0) {
          results.push({
            needId: need.id,
            tenantName: need.tenantCompany ?? need.title,
            tenantCompany: need.tenantCompany,
            requiredSqmMin: min,
            requiredSqmMax: max,
            matchScore: Math.round(partialScore * atMatch * 85),
            matchType: 'partial',
            suggestedSqm: partialSqm,
          })
        }
      }
    }

    return results
      .sort((a, b) => b.matchScore - a.matchScore)
      .slice(0, 3)
  },

  /** Find needs that a bundle of units can satisfy together */
  getMatchesForBundle(
    units: PropertyUnit[],
    property: Property,
  ): UnitNeedMatch[] {
    const combinedSqm = units.reduce((sum, u) => sum + (u.offeredSqm ?? u.areaSqm), 0)
    const results: UnitNeedMatch[] = []

    for (const need of mockLatentNeeds) {
      const { min, max } = need.sizeRange
      const atMatch = assetTypeMatch(property, String(need.assetType))
      if (atMatch === 0) continue

      const score = areaScore(combinedSqm, min, max)
      if (score > 0) {
        results.push({
          needId: need.id,
          tenantName: need.tenantCompany ?? need.title,
          tenantCompany: need.tenantCompany,
          requiredSqmMin: min,
          requiredSqmMax: max,
          matchScore: Math.round(score * atMatch * 90),
          matchType: 'bundle',
        })
      }
    }

    return results
      .sort((a, b) => b.matchScore - a.matchScore)
      .slice(0, 3)
  },

  /** Build a UnitBundle descriptor from selected units */
  buildBundle(units: PropertyUnit[]): UnitBundle {
    const floorLabel = (u: PropertyUnit) => {
      if (u.floorLevel === 0) return 'EG'
      if (u.floorLevel < 0) return `UG${Math.abs(u.floorLevel)}`
      return `${u.floorLevel}.OG`
    }
    return {
      unitIds: units.map(u => u.id),
      combinedSqm: units.reduce((s, u) => s + (u.offeredSqm ?? u.areaSqm), 0),
      label: units.map(u => `${floorLabel(u)}${u.unitLabel ? ' ' + u.unitLabel : ''}`).join(' + '),
    }
  },
}
