/**
 * Coordinates match generation between NeedProvider, PropertyProvider, and
 * MatchProvider. Lives in the service layer so no provider needs to import
 * another provider directly.
 */
import { matchStore } from '../provider/MockupMatchProvider'
import { propertyStore } from '../mock-data/propertyStore'
import { MatchStrength, MatchStatus, RiskLevel, ResultType } from '../domain/enums'
import type { Match } from '../domain/match'
import type { MatchEngineOutput } from '../domain/scoring'
import type { Need } from '../domain/need'
import type { Property } from '../domain/property'
import { getEffectiveUnits } from '../domain/property'
import { calculateScore } from '../features/matching/scoreCalculator'
import { resolveUnitFacts } from '../lib/unitFacts'

function strengthFromScore(s: number): MatchStrength {
  if (s >= 75) return MatchStrength.STRONG
  if (s >= 55) return MatchStrength.MODERATE
  return MatchStrength.WEAK
}

function buildMatch(
  prop: Property,
  unitId: string | undefined,
  need: Need,
  output: MatchEngineOutput,
  effectiveResultType: string,
  resultId: string,
  now: string,
): Match {
  const locationFactor = output.allHardFactors.find(f => f.criterion === 'location')
  const isGoodLoc = (locationFactor?.score ?? 0) >= 70

  return {
    id: `m__${prop.id}__${unitId ?? 'prop'}__${need.id}`,
    propertyId: prop.id,
    unitId,
    needId: need.id,
    resultId,
    resultType: effectiveResultType as Match['resultType'],
    matchScore: output.finalScore,
    matchStrength: strengthFromScore(output.finalScore),
    status: MatchStatus.PENDING_REVIEW,
    scoreBreakdown: {
      hardMatchScore: output.hardMatchScore,
      softFactorScore: output.softFactorScore,
      confidenceModifier: output.confidenceModifier,
      dataQualityModifier: output.dataQualityModifier,
      totalScore: output.finalScore,
    },
    positiveFactors: output.positiveFactors,
    negativeFactors: output.negativeFactors,
    allFactors: [...output.allHardFactors, ...output.allSoftFactors],
    mustHaveEvaluation: output.mustHaveEvaluation,
    tradeoffs: output.tradeOffs ?? [],
    explainabilitySummary: isGoodLoc
      ? `${prop.location.city} trifft den Standortwunsch. Kernkriterien sind weitgehend erfüllt.`
      : `Abweichender Standort (${prop.location.city}). Nur bei Engpass im Zielgebiet empfohlen.`,
    confidenceLevel: isGoodLoc ? 0.88 : 0.60,
    riskLevel: isGoodLoc ? RiskLevel.LOW : RiskLevel.MEDIUM,
    uncertaintyIndicators: isGoodLoc ? [] : ['Standort außerhalb Präferenz'],
    organizationId: 'org-wincasa',
    createdAt: now,
    updatedAt: now,
  }
}

function scoreProperty(
  need: Need,
  prop: Property,
  overrideArea?: number,
  overridePrice?: number,
  overrideResultType?: string,
  overrideHardFacts?: Partial<NonNullable<Property['hardFacts']>>,
): MatchEngineOutput {
  if (overrideArea !== undefined || overridePrice !== undefined || overrideResultType !== undefined || overrideHardFacts !== undefined) {
    return calculateScore(need, {
      ...prop,
      areaSqm: overrideArea ?? prop.areaSqm,
      rentPricePerSqm: overridePrice ?? prop.rentPricePerSqm,
      resultType: (overrideResultType ?? prop.resultType) as ResultType,
      // Einheit-Werte überschreiben Objekt-Werte (Fallback bleibt prop.hardFacts)
      hardFacts: overrideHardFacts ? { ...prop.hardFacts, ...overrideHardFacts } : prop.hardFacts,
    })
  }
  return calculateScore(need, prop)
}

// Externe Inserate aus Drittquellen (Scrapes) sind keine Plattform-Inventar-Objekte und
// gehören nicht in die Treffer (nur eigene Plattform-Objekte, Maison Work, Future Availability).
const EXTERNAL_SCRAPE_SOURCES = new Set(['HOMEGATE_SCRAPE', 'IMMOSCOUT_SCRAPE', 'NEWHOME_SCRAPE', 'MATCHOFFICE_SCRAPE'])

function isExternalListing(prop: Property): boolean {
  return prop.resultType === ResultType.VERIFIED_PORTFOLIO
    && !!prop.sourceType && EXTERNAL_SCRAPE_SOURCES.has(prop.sourceType)
}

/** Generate matches for a single need against all properties and push them into matchStore. */
export function generateMatchesForNeed(need: Need): void {
  const now = new Date().toISOString()
  const MIN_SCORE = 22

  for (const prop of propertyStore) {
    // Externe Scrape-Inserate, die fälschlich als Plattform-Objekt getaggt sind → nicht matchen
    if (isExternalListing(prop)) continue
    const hasExplicitUnits = (prop.units ?? []).length > 0

    if (hasExplicitUnits) {
      const allPreMarket = prop.units!.every(u => u.schattenmarktRelease?.enabled)
      if (!allPreMarket) {
        const output = scoreProperty(need, prop)
        if (!output.excluded && output.finalScore >= MIN_SCORE) {
          matchStore.push(buildMatch(prop, undefined, need, output, prop.resultType ?? 'VERIFIED_PORTFOLIO', prop.id, now))
        }
      }
      for (const unit of prop.units!) {
        if (!unit.schattenmarktRelease?.enabled) continue
        const facts = resolveUnitFacts(prop, unit)
        // Pre-Market: erwarteter künftiger Preis hat Vorrang vor der heutigen Sollmiete
        const unitPrice = unit.expectedRentPerSqm ?? unit.rentPricePerSqm ?? prop.rentPricePerSqm
        const unitOutput = scoreProperty(need, prop, unit.areaSqm, unitPrice, ResultType.FUTURE_AVAILABILITY, {
          fitOut: facts.fitOut,
          mieterausbaubeitragPerSqm: facts.mabPerSqm,
          fitOutByLandlord: facts.fitOutByLandlord,
          parking: facts.parkingSpots,
        })
        if (unitOutput.excluded || unitOutput.finalScore < MIN_SCORE) continue
        const resultId = `schattenmarkt-${prop.id}-${unit.id}`
        matchStore.push(buildMatch(prop, unit.id, need, unitOutput, ResultType.FUTURE_AVAILABILITY, resultId, now))
      }
    } else {
      for (const unit of getEffectiveUnits(prop)) {
        const isPreMarket = unit.schattenmarktRelease?.enabled === true
        const isFutureProp = prop.resultType === ResultType.FUTURE_AVAILABILITY
        const effectiveResultType = (isPreMarket || isFutureProp)
          ? ResultType.FUTURE_AVAILABILITY
          : (prop.resultType ?? ResultType.VERIFIED_PORTFOLIO)
        const output = scoreProperty(need, prop, unit.areaSqm, unit.rentPricePerSqm ?? prop.rentPricePerSqm, effectiveResultType)
        if (output.excluded || output.finalScore < MIN_SCORE) continue
        const resultId = isPreMarket ? `schattenmarkt-${prop.id}` : prop.id
        matchStore.push(buildMatch(prop, undefined, need, output, effectiveResultType, resultId, now))
      }
    }
  }
}

/** Remove all existing matches for a need and regenerate them (used after a need update). */
export function syncMatchesForNeed(need: Need): void {
  for (let i = matchStore.length - 1; i >= 0; i--) {
    if (matchStore[i].needId === need.id) matchStore.splice(i, 1)
  }
  generateMatchesForNeed(need)
}
