import { describe, it, expect } from 'vitest'
import { softFactorEnrichmentService } from '../../../services/softFactorEnrichmentService'
import { ResultType } from '../../../domain/enums'
import { makeProperty } from './fixtures'
import type { Property } from '../../../domain/property'

// Helper: create a property with only location context — no address that could
// accidentally trigger another location's keyword rule (e.g. 'Bahnhofstrasse'
// in the default fixture triggers the Zürich CBD rule for any city).
function locProp(city: string, district?: string): Property {
  return makeProperty({
    location: { city, district: district ?? '', country: 'CH' },
    address: { street: 'Teststrasse', houseNumber: '1', postalCode: '0000', city, country: 'CH' },
  })
}

// ── Exact keyword match — Zürich CBD ──────────────────────────────────────────

describe('softFactorEnrichmentService — exact keyword match (Zürich CBD)', () => {
  it('returns prestige 0.92 for Zürich CBD (Bahnhofstrasse address keyword)', () => {
    const prop = makeProperty() // default fixture has address.street: 'Bahnhofstrasse' → CBD match
    const result = softFactorEnrichmentService.estimate('prestige', prop)
    expect(result).not.toBeNull()
    expect(result!.score).toBeCloseTo(0.92, 1)
  })

  it('returns very high accessibility (≥ 0.95) for Zürich CBD', () => {
    const prop = makeProperty()
    const result = softFactorEnrichmentService.estimate('accessibility', prop)
    expect(result!.score).toBeGreaterThanOrEqual(0.95)
  })

  it('returns a non-empty label string', () => {
    const result = softFactorEnrichmentService.estimate('prestige', makeProperty())
    expect(result!.label).toBeTruthy()
    expect(typeof result!.label).toBe('string')
  })
})

// ── Zürich-West tech cluster ──────────────────────────────────────────────────

describe('softFactorEnrichmentService — Zürich-West tech cluster (Kreis 5)', () => {
  it('returns talentAccess ≥ 0.90 for Zürich-West Technopark', () => {
    // Use 'kreis 5' as district keyword — address.city = 'Zürich' is neutral
    const prop = locProp('Zürich', 'Kreis 5')
    const result = softFactorEnrichmentService.estimate('talentAccess', prop)
    expect(result!.score).toBeGreaterThanOrEqual(0.90)
  })

  it('returns higher ESG score for Zürich-West (0.75) than Zürich CBD (0.52)', () => {
    const cbd  = makeProperty() // CBD via Bahnhofstrasse address
    const west = locProp('Zürich', 'Kreis 5')
    const cbdEsg  = softFactorEnrichmentService.estimate('esg', cbd)
    const westEsg = softFactorEnrichmentService.estimate('esg', west)
    expect(westEsg!.score).toBeGreaterThan(cbdEsg!.score)
  })

  it('returns higher flexibility score for Zürich-West than Zürich CBD', () => {
    const cbd  = makeProperty()
    const west = locProp('Zürich', 'Kreis 5')
    expect(softFactorEnrichmentService.estimate('flexibility', west)!.score)
      .toBeGreaterThan(softFactorEnrichmentService.estimate('flexibility', cbd)!.score)
  })
})

// ── City-level fuzzy match ────────────────────────────────────────────────────

describe('softFactorEnrichmentService — city-level fallback', () => {
  it('returns a result for "Zürich" city without a district', () => {
    const result = softFactorEnrichmentService.estimate('accessibility', locProp('Zürich'))
    expect(result).not.toBeNull()
    expect(result!.score).toBeGreaterThan(0)
    expect(result!.score).toBeLessThanOrEqual(1)
  })

  it('returns a result for Basel city', () => {
    const result = softFactorEnrichmentService.estimate('talentAccess', locProp('Basel'))
    expect(result).not.toBeNull()
    expect(result!.score).toBeGreaterThan(0)
  })

  it('returns taxEnvironment > 0.90 for Zug (lowest taxes in CH)', () => {
    const result = softFactorEnrichmentService.estimate('taxEnvironment', locProp('Zug'))
    expect(result!.score).toBeGreaterThan(0.90)
  })

  it('Zug prestige is higher than Winterthur prestige', () => {
    const zugResult  = softFactorEnrichmentService.estimate('prestige', locProp('Zug'))
    const wintResult = softFactorEnrichmentService.estimate('prestige', locProp('Winterthur'))
    expect(zugResult!.score).toBeGreaterThan(wintResult!.score)
  })
})

// ── Swiss generic fallback ────────────────────────────────────────────────────

describe('softFactorEnrichmentService — Swiss generic fallback', () => {
  it('returns a non-null estimate for an unknown Swiss city', () => {
    const result = softFactorEnrichmentService.estimate('prestige', locProp('Münsingen'))
    expect(result).not.toBeNull()
    expect(result!.score).toBeGreaterThan(0)
    expect(result!.score).toBeLessThanOrEqual(1)
  })

  it('label includes "Schweiz" for generic Swiss fallback', () => {
    // 'Kleindorf' matches no rule → Swiss generic fallback
    const result = softFactorEnrichmentService.estimate('accessibility', locProp('Kleindorf'))
    expect(result!.label).toMatch(/Schweiz/i)
  })
})

// ── Score range invariant ─────────────────────────────────────────────────────

describe('softFactorEnrichmentService — score range invariant (all keys, 0–1)', () => {
  const locations = ['Zürich', 'Basel', 'Zug', 'Bern'] as const
  const keys = ['prestige', 'accessibility', 'talentAccess', 'esg', 'taxEnvironment', 'footfall'] as const

  for (const city of locations) {
    for (const key of keys) {
      it(`score is 0–1 for ${key} in ${city}`, () => {
        const result = softFactorEnrichmentService.estimate(key, locProp(city))
        if (result !== null) {
          expect(result.score).toBeGreaterThanOrEqual(0)
          expect(result.score).toBeLessThanOrEqual(1)
        }
      })
    }
  }
})

// ── Pre-Market vs Market Signal enrichment ────────────────────────────────────

describe('softFactorEnrichmentService — Pre-Market vs Market Signal', () => {
  it('returns identical estimate for same location regardless of resultType', () => {
    // The enrichment service is location-only — resultType is irrelevant.
    // FUTURE_AVAILABILITY (pre-market) and VERIFIED_PORTFOLIO should produce the same soft factor score.
    const sharedLocation = { city: 'Zürich', district: 'Oerlikon', country: 'CH' }
    const sharedAddress  = { street: 'Thurgauerstrasse', houseNumber: '1', postalCode: '8050', city: 'Zürich', country: 'CH' }

    const futureSignal = makeProperty({
      location: sharedLocation,
      address: sharedAddress,
      resultType: ResultType.FUTURE_AVAILABILITY,
    })
    const portfolioResult = makeProperty({
      location: sharedLocation,
      address: sharedAddress,
      resultType: ResultType.VERIFIED_PORTFOLIO,
    })

    const futureEst = softFactorEnrichmentService.estimate('accessibility', futureSignal)
    const portfolioEst = softFactorEnrichmentService.estimate('accessibility', portfolioResult)
    expect(futureEst?.score).toBe(portfolioEst?.score)
    expect(futureEst?.label).toBe(portfolioEst?.label)
  })

  it('FUTURE_AVAILABILITY at Oerlikon gets meaningful accessibility score (> 0.70)', () => {
    const prop = makeProperty({
      location: { city: 'Zürich', district: 'Oerlikon', country: 'CH' },
      address: { street: 'Thurgauerstrasse', houseNumber: '40', postalCode: '8050', city: 'Zürich', country: 'CH' },
      resultType: ResultType.FUTURE_AVAILABILITY,
    })
    const result = softFactorEnrichmentService.estimate('accessibility', prop)
    expect(result!.score).toBeGreaterThan(0.70)
  })
})
