import type { CreateNeedInput } from '../../../domain/need'
import type { ParsedNeedCriteria, FollowUpQuestion } from '../../../domain/needBuilder'
import type { UnifiedMatchResult } from '../../../domain/unifiedResult'
import type {
  IAIService,
  AIResponse,
  OfferEmailPayload,
  MatchExplanationInput,
  TradeOffInput,
  TradeOffSummary,
  DataQualityInput,
  MarketSignalClassification,
  FitOutAdviceInput,
  FitOutAdvice,
  PreMarketRentInput,
  PreMarketRentRecommendation,
} from '../IAIService'
import { mockProvenance } from '../IAIService'
import { aiTraceStore } from '../tracing'
import { mockParseNeed } from './needParser'
import { buildComparisonSummary } from './compareBuilder'
import { buildMockDecisionBrief } from './decisionBrief'
import { getCityIntelligence, getMarketRent } from '../../../lib/locationIntelligence'

const SIMULATED_DELAY = { fast: 300, medium: 600, slow: 1800 }
const delay = (ms: number) => new Promise(r => setTimeout(r, ms))

// ── Tracing wrapper ───────────────────────────────────────────────────────────

async function traceMock<T>(method: string, fn: () => Promise<AIResponse<T>>): Promise<AIResponse<T>> {
  const startMs = Date.now()
  const result = await fn()
  aiTraceStore.add({
    id: crypto.randomUUID(),
    method,
    provider: 'mock',
    model: 'mock',
    promptVersion: 'mock',
    latencyMs: Date.now() - startMs,
    fallbackUsed: false,
    validationPassed: true,
    responseValidationStatus: 'valid',
    source: 'mock',
    createdAt: new Date().toISOString(),
  })
  return result
}

// ── Follow-up question templates keyed by ParsedNeedCriteria field ────────────

interface QuestionTemplate {
  questionText: string
  reason: string
  suggestedAnswerOptions?: string[]
  importance: FollowUpQuestion['importance']
}

const FOLLOW_UP_TEMPLATES: Partial<Record<keyof ParsedNeedCriteria, QuestionTemplate>> = {
  assetType: {
    questionText: 'Welchen Nutzungstyp suchen Sie?',
    reason: 'Nutzungstyp ist zwingend für die Matchsuche',
    suggestedAnswerOptions: ['Büro', 'Retail', 'Logistik', 'Produktion', 'Leichtindustrie', 'Gastro'],
    importance: 'required',
  },
  areaRange: {
    questionText: 'Welche Fläche benötigen Sie (min–max in m²)?',
    reason: 'Flächenbedarf ist zwingend für die Filterung',
    importance: 'required',
  },
  preferredLocations: {
    questionText: 'In welchen Städten oder Regionen suchen Sie?',
    reason: 'Standortpräferenz fehlt',
    suggestedAnswerOptions: ['Zürich', 'Basel', 'Bern', 'Zug', 'Genf', 'Lausanne'],
    importance: 'required',
  },
  budgetRange: {
    questionText: 'Was ist Ihr maximales Budget pro m² und Jahr (CHF)?',
    reason: 'Budget ist wichtig für die Filterung unpassender Objekte',
    importance: 'recommended',
  },
  timing: {
    questionText: 'Wann möchten Sie spätestens einziehen?',
    reason: 'Verfügbarkeitstermin fehlt',
    importance: 'recommended',
  },
  mustHaveCriteria: {
    questionText: 'Haben Sie zwingende Anforderungen (ÖV-Anbindung, Parkplätze, Laderampe)?',
    reason: 'Pflichtkriterien sind für die Filterung relevant',
    importance: 'optional',
  },
}

const AREA_AMBIGUITY_RATIO_THRESHOLD = 8

const AREA_AMBIGUITY_QUESTION: QuestionTemplate = {
  questionText: 'Ihre Flächenangabe ist sehr weit gefasst — können Sie den Bereich präzisieren (z.B. min 300 m², max 600 m²)?',
  reason: 'Zu grosse Spanne reduziert die Matchgenauigkeit erheblich',
  importance: 'required',
}

function isAreaAmbiguous(areaRange: NonNullable<ParsedNeedCriteria['areaRange']>): boolean {
  const { min, max } = areaRange
  if (min <= 0 || max <= 0) return true
  return max / min > AREA_AMBIGUITY_RATIO_THRESHOLD
}

// Priority order: required fields first, recommended next, optional last.
// Max 3 questions returned. Area range ambiguity is detected and raised as a
// required clarification even when areaRange is nominally present.
const FIELD_PRIORITY: Array<keyof ParsedNeedCriteria> = [
  'assetType',
  'areaRange',
  'preferredLocations',
  'budgetRange',
  'timing',
  'mustHaveCriteria',
]

function buildFollowUpQuestions(criteria: ParsedNeedCriteria): FollowUpQuestion[] {
  const questions: FollowUpQuestion[] = []
  let idx = 0

  for (const field of FIELD_PRIORITY) {
    if (questions.length >= 3) break

    if (field === 'areaRange') {
      if (!criteria.areaRange) {
        const tpl = FOLLOW_UP_TEMPLATES['areaRange']!
        questions.push({ id: `fq-mock-${idx++}`, questionText: tpl.questionText, targetField: 'areaRange', reason: tpl.reason, importance: tpl.importance })
      } else if (isAreaAmbiguous(criteria.areaRange)) {
        questions.push({ id: `fq-mock-${idx++}`, questionText: AREA_AMBIGUITY_QUESTION.questionText, targetField: 'areaRange', reason: AREA_AMBIGUITY_QUESTION.reason, importance: AREA_AMBIGUITY_QUESTION.importance })
      }
      continue
    }

    const isMissing =
      field === 'preferredLocations' ? !criteria.preferredLocations?.length
      : field === 'mustHaveCriteria' ? !criteria.mustHaveCriteria?.length
      : !criteria[field]

    if (isMissing) {
      const tpl = FOLLOW_UP_TEMPLATES[field]
      if (!tpl) continue
      questions.push({
        id:                     `fq-mock-${idx++}`,
        questionText:           tpl.questionText,
        targetField:            field,
        reason:                 tpl.reason,
        importance:             tpl.importance,
        suggestedAnswerOptions: tpl.suggestedAnswerOptions,
      })
    }
  }

  return questions
}

// ── Service ───────────────────────────────────────────────────────────────────

export const MockAIService: IAIService = {
  parseNeed: (input: string) =>
    traceMock('parseNeed', async () => {
      await delay(SIMULATED_DELAY.fast)
      return { data: mockParseNeed(input), provenance: mockProvenance() }
    }),

  generateFollowUpQuestions: (criteria: ParsedNeedCriteria) =>
    traceMock('generateFollowUpQuestions', async () => {
      await delay(SIMULATED_DELAY.medium)
      return { data: buildFollowUpQuestions(criteria), provenance: mockProvenance() }
    }),

  generateMatchExplanation: (input: MatchExplanationInput) =>
    traceMock('generateMatchExplanation', async () => {
      await delay(SIMULATED_DELAY.medium)
      const isStrong = input.matchScore >= 78
      const isMedium = input.matchScore >= 52
      const headline = isStrong
        ? `Starkes Match — ${input.propertyTitle} erfüllt Ihre Kernkriterien hervorragend`
        : isMedium
          ? `Gutes Match mit einzelnen Kompromissen für ${input.propertyTitle}`
          : `Schwaches Match — mehrere Kriterien nicht erfüllt bei ${input.propertyTitle}`
      const positiveText = input.positiveFactors.slice(0, 2).map(f => f.explanation).join('; ')
      const negativeText = input.negativeFactors.slice(0, 1).map(f => f.explanation).join('; ')
      const summary = `${input.propertyTitle} in ${input.propertyCity} erreicht ${input.matchScore}/100 Punkte.${positiveText ? ` Hauptstärken: ${positiveText}.` : ''}${negativeText ? ` Einschränkung: ${negativeText}.` : ''}`
      return {
        data: {
          headline,
          summary,
          keyReasons: [
            ...input.positiveFactors.slice(0, 2).map(f => `+ ${f.explanation}`),
            ...input.negativeFactors.slice(0, 1).map(f => `− ${f.explanation}`),
          ],
        },
        provenance: mockProvenance(),
      }
    }),

  summarizeTradeOffs: (tradeoffs: TradeOffInput[]) =>
    traceMock('summarizeTradeOffs', async () => {
      await delay(SIMULATED_DELAY.fast)
      const critical = tradeoffs.filter(t => t.severity === 'HIGH')
      const overallRisk: TradeOffSummary['overallRisk'] =
        critical.length >= 2 ? 'HIGH' : critical.length === 1 ? 'MEDIUM' : 'LOW'
      const riskLabel = overallRisk === 'HIGH' ? 'Hoch' : overallRisk === 'MEDIUM' ? 'Mittel' : 'Gering'
      return {
        data: {
          headline: tradeoffs.length === 0
            ? 'Keine wesentlichen Trade-offs identifiziert'
            : `${tradeoffs.length} Trade-off${tradeoffs.length > 1 ? 's' : ''} — Gesamtrisiko: ${riskLabel}`,
          items: tradeoffs.map(t => ({ concern: t.concern, severity: t.severity, mitigation: t.mitigation })),
          overallRisk,
        },
        provenance: mockProvenance(),
      }
    }),

  summarizeComparison: (items: UnifiedMatchResult[]) =>
    traceMock('summarizeComparison', async () => {
      await delay(SIMULATED_DELAY.medium)
      return { data: buildComparisonSummary(items), provenance: mockProvenance() }
    }),

  generateDecisionBrief: (shortlistId: string) =>
    traceMock('generateDecisionBrief', async () => {
      await delay(SIMULATED_DELAY.slow)
      return { data: buildMockDecisionBrief(shortlistId), provenance: mockProvenance() }
    }),

  generateDataQualitySummary: (_propertyId: string, quality: DataQualityInput) =>
    traceMock('generateDataQualitySummary', async () => {
      await delay(SIMULATED_DELAY.fast)
      const level =
        quality.score >= 0.85 ? 'excellent'
        : quality.score >= 0.70 ? 'good'
        : quality.score >= 0.55 ? 'fair'
        : quality.score >= 0.40 ? 'poor'
        : 'critical'
      const assessments: Record<string, string> = {
        excellent: 'Exzellente Datenqualität — alle Kernfelder vollständig und aktuell.',
        good: 'Gute Datenqualität — kleinere Lücken beeinflussen die Matchgenauigkeit nicht wesentlich.',
        fair: 'Ausreichende Datenqualität — fehlende Felder können die Matchgenauigkeit beeinträchtigen.',
        poor: 'Geringe Datenqualität — wichtige Felder fehlen, Match-Score mit Vorsicht interpretieren.',
        critical: 'Kritische Datenqualität — fundamentale Felder fehlen, Match-Ergebnis stark eingeschränkt.',
      }
      const hasCritical = quality.missingCriticalFields.length > 0
      return {
        data: {
          overallAssessment: assessments[level],
          missingCriticalFields: quality.missingCriticalFields,
          recommendation: hasCritical
            ? `Fehlende Pflichtfelder ergänzen: ${quality.missingCriticalFields.join(', ')}`
            : quality.score < 0.70
              ? 'Daten aktualisieren und optionale Felder ergänzen für bessere Matchgenauigkeit.'
              : 'Keine sofortigen Massnahmen erforderlich.',
          confidence: quality.score,
        },
        provenance: mockProvenance(),
      }
    }),

  classifyMarketSignal: (signalText: string) =>
    traceMock('classifyMarketSignal', async () => {
      await delay(SIMULATED_DELAY.medium)
      const t = signalText.toLowerCase()
      let signalType: MarketSignalClassification['signalType'] = 'UNKNOWN'
      if (t.includes('neubau') || t.includes('baubewilligung') || t.includes('umbau')) signalType = 'CONSTRUCTION'
      else if (t.includes('expansion') || t.includes('wachstum') || t.includes('sucht fläche')) signalType = 'EXPANSION'
      else if (t.includes('verlegt') || t.includes('umzug') || t.includes('relocation')) signalType = 'RELOCATION'
      else if (t.includes('stellenabbau') || t.includes('restruktur') || t.includes('fusion')) signalType = 'RESTRUCTURING'
      else if (t.includes('frei') || t.includes('kündigung') || t.includes('schliessung') || t.includes('leerstand')) signalType = 'VACANCY'
      const areaMatch = signalText.match(/(\d{2,5})\s*m²/)
      const monthsMatch = signalText.match(/(\d{1,2})\s*Monate?n?/)
      return {
        data: {
          signalType,
          probability: 0.65,
          timeHorizonMonths: monthsMatch ? parseInt(monthsMatch[1]) : null,
          areaSqmEstimate: areaMatch ? parseInt(areaMatch[1]) : null,
          credibility: 'MEDIUM',
          reasoning: `Keyword-basierte Klassifikation (Mock). Signaltyp: ${signalType}.`,
        },
        provenance: mockProvenance(),
      }
    }),

  generateOfferEmail: (payload: OfferEmailPayload) =>
    traceMock('generateOfferEmail', async () => {
      await delay(SIMULATED_DELAY.medium * 2)
      return {
        data: {
          subject: `Passende Gewerbeflächen zu Ihrer Anfrage: ${payload.needTitle}`,
          body:
            `Sehr geehrte Damen und Herren,\n\nvielen Dank für Ihr Interesse. Gerne unterbreiten wir Ihnen folgende passende Gewerbeobjekte aus unserem Portfolio:\n\n` +
            payload.properties.map((p, i) => `• ${p} (Match-Score: ${payload.matchScores[i]}%)`).join('\n') +
            `\n\nGerne arrangieren wir Besichtigungstermine für die genannten Objekte und stehen für alle weiteren Fragen zur Verfügung.\n\nFreundliche Grüsse\nWincasa AG`,
        },
        provenance: mockProvenance(),
      }
    }),

  generateFitOutAdvice: (input: FitOutAdviceInput) =>
    traceMock('generateFitOutAdvice', async () => {
      await delay(SIMULATED_DELAY.medium)
      const mab = input.mabPerSqm
      const fitOut = input.fitOut

      let recommendation: FitOutAdvice['recommendation']
      let headline: string
      let explanation: string
      let negotiationTip: string

      if (fitOut === 'SHELL') {
        if (mab >= 300) {
          recommendation = 'MAB_AMORTISATION'
          headline = 'MAB-Amortisation empfohlen — Vermieter trägt Grossteil der Ausbaukosten'
          explanation = `Mit CHF ${mab}/m² MAB übernimmt der Vermieter einen erheblichen Teil der Ausbauinvestition. Die verbleibende Nettoinvestition wird über die Vertragslaufzeit amortisiert. Für ${input.areaSqm.toLocaleString('de-CH')} m² Rohbaufläche ist dies die kosteneffizienteste Lösung.`
          negotiationTip = 'Verhandeln Sie eine höhere MAB-Rate gegen eine längere Mietvertragslaufzeit (min. 5 Jahre).'
        } else {
          recommendation = 'BKZ'
          headline = 'Baukostenzuschuss (BKZ) verhandeln — Vermieter zahlt Ausbaupauschale'
          explanation = `Bei SHELL-Übergabe ohne wesentlichem MAB ist ein Baukostenzuschuss (BKZ) die effektivste Option. Der Vermieter zahlt einen einmaligen Betrag, den Sie für den Innenausbau nutzen. Typisch sind CHF 200–400/m² als BKZ.`
          negotiationTip = `Fordern Sie CHF ${Math.round(300 * input.areaSqm / 1000) * 1000}.– als BKZ-Pauschale. Reichen Sie Ausbauofferten von 2 Generalunternehmern vor der Unterzeichnung ein.`
        }
      } else {
        recommendation = 'MIETERAUSBAU'
        headline = 'Mieterausbau auf eigene Rechnung — geringe Restinvestition'
        explanation = `${input.fitOut === 'BASIC' ? 'Basisausbau' : 'Vollausbau'} erfordert nur noch Anpassungen nach Ihren Bedürfnissen. Die Investition ist überschaubar und amortisiert sich bei einer Mietdauer von 3+ Jahren.`
        negotiationTip = 'Lassen Sie eine Ausbauklausel im Mietvertrag festhalten: Entfernung von Mieterausbauten bei Auszug nur auf explizite Anforderung des Vermieters.'
      }

      const grossMin = (fitOut === 'SHELL' ? 800 : 400) - mab
      const grossMax = (fitOut === 'SHELL' ? 1500 : 800) - mab
      const netMin = Math.max(0, grossMin)
      const netMax = Math.max(0, grossMax)
      const estimatedNetInvestment = netMax <= 0
        ? 'Vollständig durch MAB gedeckt'
        : `CHF ${Math.round(netMin * input.areaSqm / 1000) * 1000}–${Math.round(netMax * input.areaSqm / 1000) * 1000}.–`

      const data: FitOutAdvice = { recommendation, headline, explanation, negotiationTip, estimatedNetInvestment }
      return { data, provenance: mockProvenance() }
    }),

  // ── recommendPreMarketRent ──────────────────────────────────────────────────
  recommendPreMarketRent: (input: PreMarketRentInput) =>
    traceMock('recommendPreMarketRent', async () => {
      await delay(SIMULATED_DELAY.fast)
      const intel = getCityIntelligence(input.city)
      const comp = getMarketRent(input.city, input.assetType)
      const current = input.currentRentPerSqm
      const ASSET_LABELS: Record<string, string> = { OFFICE: 'Bürofläche', LOGISTICS: 'Logistikfläche', LIGHT_INDUSTRIAL: 'Gewerbefläche', RETAIL: 'Retailfläche', PRODUCTION: 'Produktionsfläche' }
      const assetLabel = ASSET_LABELS[input.assetType] ?? 'Fläche'

      // Ohne regionale Vergleichsdaten: nur grobe Schätzung, niedrige Konfidenz
      if (!intel || comp == null) {
        const rec = Math.round(current * 1.02)
        const data: PreMarketRentRecommendation = {
          recommendedPerSqm: rec, rangeMinPerSqm: Math.round(rec * 0.93), rangeMaxPerSqm: Math.round(rec * 1.07),
          verdict: 'FAIR', deltaVsCurrentPct: 0,
          drivers: ['Keine regionalen Vergleichsdaten verfügbar'],
          rationale: 'Keine ausreichenden Marktdaten für diese Region — Empfehlung beruht auf dem heutigen Preis.',
          confidence: 'LOW',
        }
        return { data, provenance: mockProvenance() }
      }

      // Empfehlung am HEUTIGEN Preis des Objekts verankert (realistisch) und nur begrenzt
      // nach Marktmomentum (Angebot/Nachfrage/Trend) angepasst — keine Sprünge auf den
      // stadtweiten Median (segment-grob). Vergleichsmiete dient nur als Spielraum-Check.
      let adj = intel.rentTrend12m / 100   // Trend vorwärts (Pre-Market liegt in der Zukunft)
      if (intel.vacancyRatePct < 2.5) adj += 0.04
      else if (intel.vacancyRatePct < 4) adj += 0.02
      else if (intel.vacancyRatePct > 5.5) adj -= 0.05
      else if (intel.vacancyRatePct > 4.5) adj -= 0.02
      adj += { VERY_HIGH: 0.05, HIGH: 0.025, MEDIUM: 0, LOW: -0.04 }[intel.demandStrength]
      if (intel.avgDaysOnMarket < 35) adj += 0.015
      else if (intel.avgDaysOnMarket > 75) adj -= 0.025
      // Spielraum-Check: liegt der heutige Preis bereits über dem regionalen Marktband → kaum Luft nach oben
      if (comp != null && current >= comp) adj = Math.min(adj, 0.02)
      adj = Math.max(-0.10, Math.min(0.15, adj))   // realistischer Rahmen: −10 % … +15 %

      const recommended = Math.round(current * (1 + adj))
      const rangeMin = Math.round(recommended * 0.95)
      const rangeMax = Math.round(recommended * 1.05)
      const deltaVsCurrentPct = Math.round(adj * 100)
      const verdict: PreMarketRentRecommendation['verdict'] =
        deltaVsCurrentPct >= 5 ? 'UNDERPRICED' : deltaVsCurrentPct <= -4 ? 'AMBITIOUS' : 'FAIR'

      const supplyLabel = intel.vacancyRatePct < 3 ? 'sehr knappes Angebot' : intel.vacancyRatePct > 5 ? 'entspanntes Angebot' : 'ausgeglichenes Angebot'
      const demandLabel = { VERY_HIGH: 'sehr hohe Nachfrage', HIGH: 'hohe Nachfrage', MEDIUM: 'mittlere Nachfrage', LOW: 'schwache Nachfrage' }[intel.demandStrength]
      const drivers = [
        `Leerstand ${intel.vacancyRatePct}% (${supplyLabel})`,
        demandLabel,
        `Miettrend ${intel.rentTrend12m >= 0 ? '+' : ''}${intel.rentTrend12m}% (12 M)`,
        `Ø Vermietungsdauer ${intel.avgDaysOnMarket} Tage`,
      ]
      const verdictText =
        verdict === 'UNDERPRICED' ? `Marktumfeld lässt Spielraum nach oben (+${deltaVsCurrentPct}% ggü. heute).`
        : verdict === 'AMBITIOUS' ? `Marktumfeld eher schwächer (${deltaVsCurrentPct}% ggü. heute) — vorsichtig ansetzen.`
        : 'Heutiger Preis ist marktgerecht.'
      const rationale = `${assetLabel} in ${input.city}: ${supplyLabel}, ${demandLabel}, Miettrend ${intel.rentTrend12m >= 0 ? '+' : ''}${intel.rentTrend12m}%. Empfehlung für Pre-Market: CHF ${recommended}/m² (CHF ${rangeMin}–${rangeMax}) — verankert am heutigen Preis CHF ${current}/m². ${verdictText}`
      const confidence: PreMarketRentRecommendation['confidence'] =
        intel.demandStrength === 'LOW' || intel.avgDaysOnMarket > 75 ? 'MEDIUM' : 'HIGH'

      const data: PreMarketRentRecommendation = {
        recommendedPerSqm: recommended, rangeMinPerSqm: rangeMin, rangeMaxPerSqm: rangeMax,
        verdict, deltaVsCurrentPct, drivers, rationale, confidence,
      }
      return { data, provenance: mockProvenance() }
    }),

  // Legacy methods
  extractCriteria: (_input: string) =>
    traceMock('extractCriteria', async () => ({
      data: {
        extractedCriteria: {
          companyName: 'Unbekannt (bitte bestätigen)',
          requiredArea: { min: 400, max: 900 },
          budgetRange: { maxPerSqm: 40, currency: 'CHF' },
        },
        confidence: 0.72,
        missingFields: ['assetType', 'timing', 'preferredLocations'],
        assumptions: ['Fläche aus Zahlenangabe geschätzt', 'Budget aus Kostennennung abgeleitet'],
        followUpQuestions: [
          'Welchen Nutzungstyp suchen Sie (Büro, Retail, Logistik)?',
          'In welchen Städten oder Regionen suchen Sie?',
          'Wann möchten Sie spätestens einziehen?',
        ],
      },
      provenance: mockProvenance(),
    })),

  generateFollowUp: (partialNeed: Partial<CreateNeedInput>) =>
    traceMock('generateFollowUp', async () => {
      const questions: string[] = []
      if (!partialNeed.assetType) questions.push('Welchen Nutzungstyp suchen Sie?')
      if (!partialNeed.preferredLocations?.length) questions.push('In welchen Regionen suchen Sie?')
      if (!partialNeed.timing) questions.push('Was ist Ihr gewünschter Einzugstermin?')
      if (!partialNeed.budgetRange) questions.push('Was ist Ihr maximales monatliches Budget?')
      return { data: questions, provenance: mockProvenance() }
    }),
}
