import { MockupMatchProvider } from '../provider/MockupMatchProvider'
import { MockupPropertyProvider } from '../provider/MockupPropertyProvider'
import { MockupNeedProvider } from '../provider/MockupNeedProvider'
import { MockupInquiryProvider } from '../provider/MockupInquiryProvider'
import type { MatchFilters } from '../provider/IMatchProvider'
import type { Match, PropertyNeedMatch } from '../domain/match'
import type { Need } from '../domain/need'
import type { Property } from '../domain/property'
import type { StrongMatchItem } from '../domain/dashboard'
import type { ScoreBreakdown } from '../domain/match'
import type { AdditionalPropertyMatch } from '../domain/additionalMatch'
import type { ListResponse, ItemResponse } from './types'
import { buildFullMatch, computeRankedMatches } from '../features/matching/rankingEngine'
import { ResultType } from '../domain/enums'
import { throwServiceError } from './errors'

const provider = MockupMatchProvider

export const matchService = {
  async getAll(filters?: MatchFilters): Promise<ListResponse<Match>> {
    try {
      const data = await provider.getAll(filters)
      return { data, meta: { total: data.length, page: 1, pageSize: data.length, hasMore: false } }
    } catch (err) {
      throwServiceError(err)
    }
  },
  async getById(id: string): Promise<ItemResponse<Match | null>> {
    try {
      const data = await provider.getById(id)
      return { data }
    } catch (err) {
      throwServiceError(err)
    }
  },
  async getByNeed(needId: string): Promise<ListResponse<Match>> {
    try {
      const data = await provider.getByNeed(needId)
      return { data, meta: { total: data.length, page: 1, pageSize: data.length, hasMore: false } }
    } catch (err) {
      throwServiceError(err)
    }
  },
  async getByProperty(propertyId: string): Promise<ListResponse<Match>> {
    try {
      const data = await provider.getByProperty(propertyId)
      return { data, meta: { total: data.length, page: 1, pageSize: data.length, hasMore: false } }
    } catch (err) {
      throwServiceError(err)
    }
  },
  async approve(id: string, reviewedBy: string): Promise<ItemResponse<Match>> {
    try {
      const data = await provider.approve(id, reviewedBy)
      return { data }
    } catch (err) {
      throwServiceError(err)
    }
  },

  async getMatchesForProperty(propertyId: string): Promise<ListResponse<Match>> {
    try {
      const data = await provider.getByProperty(propertyId)
      return { data, meta: { total: data.length, page: 1, pageSize: data.length, hasMore: false } }
    } catch (err) {
      throwServiceError(err)
    }
  },

  async getMatchDetail(id: string): Promise<ItemResponse<Match | null>> {
    try {
      const data = await provider.getById(id)
      return { data }
    } catch (err) {
      throwServiceError(err)
    }
  },

  async getScoreBreakdown(matchId: string): Promise<ItemResponse<ScoreBreakdown | null>> {
    try {
      const match = await provider.getById(matchId)
      return { data: match?.scoreBreakdown ?? null }
    } catch (err) {
      throwServiceError(err)
    }
  },

  // ── Engine-based methods ──────────────────────────────────────────────────

  computeMatch(need: Need, property: Property): Match {
    return buildFullMatch(need, property)
  },

  async computeMatchesForNeed(needId: string): Promise<ListResponse<Match>> {
    try {
      const [need, properties] = await Promise.all([
        MockupNeedProvider.getById(needId),
        MockupPropertyProvider.getAll(),
      ])
      if (!need) return { data: [], meta: { total: 0, page: 1, pageSize: 0, hasMore: false } }
      const data = computeRankedMatches(need, properties)
      return { data, meta: { total: data.length, page: 1, pageSize: data.length, hasMore: false } }
    } catch (err) {
      throwServiceError(err)
    }
  },

  async getNeedMatchesForProperty(
    propertyId: string,
    opts?: { minScore?: number },
  ): Promise<PropertyNeedMatch[]> {
    try {
      const minScore = opts?.minScore ?? 80
      const [matches, needs] = await Promise.all([
        provider.getByProperty(propertyId),
        MockupNeedProvider.getAll(),
      ])
      return matches
        .filter(m => m.matchScore >= minScore)
        .sort((a, b) => b.matchScore - a.matchScore)
        .map(m => {
          const need = needs.find(n => n.id === m.needId)
          if (!need) return null
          return {
            matchId: m.id,
            score: m.matchScore,
            needId: need.id,
            needTitle: `${need.companyName}`,
            company: need.companyName,
            areaRequired: `${need.requiredArea.min}–${need.requiredArea.max} m²`,
            budget: `max ${need.budgetRange.maxPerSqm} CHF/m²/Jahr`,
            locations: need.preferredLocations,
            assetType: need.assetType,
            timing: need.timing?.earliestMoveIn ?? '–',
            mustHaves: need.mustCriteriaText ?? [],
            matchHighlights: m.positiveFactors.map(f => f.explanation),
          } satisfies PropertyNeedMatch
        })
        .filter((x): x is PropertyNeedMatch => x !== null)
    } catch (err) {
      throwServiceError(err)
    }
  },

  async getAdditionalMatchesForInquiry(
    inquiryId: string,
    opts?: { minScore?: number; excludePropertyId?: string },
  ): Promise<AdditionalPropertyMatch[]> {
    try {
      const minScore = opts?.minScore ?? 80
      const inquiry = await MockupInquiryProvider.getById(inquiryId)
      if (!inquiry) return []
      const excludeId = opts?.excludePropertyId ?? inquiry.propertyId
      const [allProperties, allMatches] = await Promise.all([
        MockupPropertyProvider.getAll(),
        provider.getAll(),
      ])
      const portfolioProperties = allProperties.filter(
        p => p.resultType === ResultType.VERIFIED_PORTFOLIO && p.id !== excludeId,
      )
      return portfolioProperties
        .map(p => {
          const match = allMatches.find(m => m.propertyId === p.id)
          const score = match?.matchScore ?? 0
          return { property: p, score, match }
        })
        .filter(({ score }) => score >= minScore)
        .sort((a, b) => b.score - a.score)
        .slice(0, 5)
        .map(({ property: p, score, match }) => ({
          propertyId: p.id,
          title: p.title,
          location: `${p.location.city}${p.location.district ? `, ${p.location.district}` : ''}`,
          areaSqm: p.areaSqm,
          rentPricePerSqm: p.rentPricePerSqm,
          matchScore: score,
          imageUrl: p.images?.[0],
          reasons: (match?.positiveFactors ?? []).slice(0, 3).map(f => f.explanation ?? f.criterion ?? '').filter(Boolean),
          topUncertainty: (match?.tradeoffs ?? match?.tradeOffs ?? [])[0]?.concern
            ?? (match?.negativeFactors ?? [])[0]?.explanation
            ?? undefined,
        }))
    } catch (err) {
      throwServiceError(err)
    }
  },

  async getStrongMatches(minScore = 80): Promise<StrongMatchItem[]> {
    try {
      const [matches, properties] = await Promise.all([
        provider.getAll(),
        MockupPropertyProvider.getAll(),
      ])
      return matches
        .filter(m => m.matchScore >= minScore)
        .slice(0, 5)
        .map(m => {
          const prop = properties.find(p => p.id === m.propertyId)
          const topFactor = m.positiveFactors?.[0]
          const firstAction = m.nextBestActions?.[0]
          return {
            matchId: m.id,
            propertyId: m.propertyId,
            propertyTitle: prop?.title ?? 'Unbekanntes Objekt',
            propertyAddress: prop?.address
              ? `${prop.address.street} ${prop.address.houseNumber}, ${prop.address.city}`
              : '–',
            needSummary: m.needId,
            matchScore: m.matchScore,
            topReason: topFactor?.explanation ?? topFactor?.criterion ?? '–',
            missingDataCount: m.missingData?.length ?? 0,
            nextBestAction: firstAction?.label ?? '–',
          } satisfies StrongMatchItem
        })
    } catch (err) {
      throwServiceError(err)
    }
  },
}
