// ── Uber-style job offer pool ────────────────────────────────────────────────
// Manages the offer lifecycle: create → broadcast → accept/decline → assign
// Supports both push (dispatcher sends to specific techs) and pull (tech picks from pool)
// Pricing: rush/overtime jobs carry displacement fee + hourly rate
// ─────────────────────────────────────────────────────────────────────────────
import { ref, computed } from 'vue'
import { fetchActiveOffers, createOffer, acceptOffer, declineOffer, cancelOffer } from 'src/api/offers'
import { updateJob } from 'src/api/dispatch'
import { sendTestSms } from 'src/api/sms'
import { localDateStr, techDaySchedule, techDayCapacityH, timeToH } from './useHelpers'

// ── Pricing presets ──────────────────────────────────────────────────────────
export const PRICING_PRESETS = {
  rush_weekend: {
    label: 'Rush fin de semaine',
    displacement: 150,
    hourlyRate: 125,
    currency: 'CAD',
    description: 'Déplacement minimum 150$ + 125$/h',
  },
  rush_evening: {
    label: 'Rush soirée',
    displacement: 100,
    hourlyRate: 100,
    currency: 'CAD',
    description: 'Déplacement minimum 100$ + 100$/h',
  },
  rush_holiday: {
    label: 'Rush jour férié',
    displacement: 200,
    hourlyRate: 150,
    currency: 'CAD',
    description: 'Déplacement minimum 200$ + 150$/h',
  },
  standard: {
    label: 'Tarif régulier',
    displacement: 0,
    hourlyRate: 0,
    currency: 'CAD',
    description: 'Inclus dans le plan de service',
  },
}

export function useJobOffers (store) {
  const offers = ref([])
  const loadingOffers = ref(false)
  const showOfferPool = ref(false)

  // ── Map raw ERPNext doc → local offer object ──────────────────────────────
  function _mapOffer (o) {
    return {
      id: o.name,
      name: o.name,
      jobName: o.job_name || null,           // linked Dispatch Job
      subject: o.subject || '',
      address: o.address || '',
      customer: o.customer || '',
      scheduledDate: o.scheduled_date || null,
      startTime: o.start_time || null,
      duration: parseFloat(o.duration_h) || 1,
      priority: o.priority || 'medium',
      status: o.status || 'open',            // open | pending | accepted | expired | cancelled
      // Targeting
      offerMode: o.offer_mode || 'broadcast', // broadcast | targeted | pool
      targetTechs: o.target_techs ? JSON.parse(o.target_techs) : [],  // specific tech IDs
      requiredTags: o.required_tags ? JSON.parse(o.required_tags) : [],
      // Responses
      acceptedBy: o.accepted_by || null,
      acceptedAt: o.accepted_at || null,
      declinedTechs: o.declined_techs ? JSON.parse(o.declined_techs) : [],
      // Pricing
      pricingPreset: o.pricing_preset || 'standard',
      displacement: parseFloat(o.displacement_fee) || 0,
      hourlyRate: parseFloat(o.hourly_rate) || 0,
      currency: o.currency || 'CAD',
      // Customer checkout
      isCustomerRequest: !!o.is_customer_request,
      salesOrder: o.sales_order || null,
      // Timing
      expiresAt: o.expires_at || null,
      createdAt: o.creation || null,
      // Source
      orderSource: o.order_source || 'dispatch', // dispatch | portal | api
    }
  }

  // ── Load active offers ────────────────────────────────────────────────────
  async function loadOffers () {
    loadingOffers.value = true
    try {
      const raw = await fetchActiveOffers()
      offers.value = raw.map(_mapOffer)
    } catch (e) {
      console.warn('[offers] load failed:', e.message)
    } finally {
      loadingOffers.value = false
    }
  }

  // ── Active offers count (for badge) ───────────────────────────────────────
  const activeOfferCount = computed(() => offers.value.filter(o => o.status === 'open' || o.status === 'pending').length)

  // ── Find matching techs for an offer ──────────────────────────────────────
  function matchingTechs (offer) {
    if (!store.technicians?.length) return []
    return store.technicians.filter(tech => {
      // Skip inactive / absent
      if (tech.status === 'off' || tech.status === 'inactive') return false
      // Skip already declined
      if (offer.declinedTechs.some(d => d.techId === tech.id)) return false
      // Check required tags/skills
      if (offer.requiredTags.length) {
        const hasAll = offer.requiredTags.every(reqTag => {
          const techTag = tech.tagsWithLevel?.find(t => t.tag === reqTag.tag || t.tag === reqTag)
          if (!techTag) return false
          if (reqTag.level && techTag.level < reqTag.level) return false
          return true
        })
        if (!hasAll) return false
      }
      // Check availability on scheduled date
      if (offer.scheduledDate) {
        const sched = techDaySchedule(tech, offer.scheduledDate)
        // For rush/overtime: tech doesn't need to be on schedule
        if (!sched && offer.pricingPreset === 'standard') return false
        // Check capacity (allow overflow for rush)
        if (sched && offer.pricingPreset === 'standard') {
          const cap = techDayCapacityH(tech, offer.scheduledDate)
          const load = tech.queue
            .filter(j => j.scheduledDate === offer.scheduledDate)
            .reduce((sum, j) => sum + (parseFloat(j.duration) || 0), 0)
          if (load + offer.duration > cap * 1.2) return false // 20% overflow tolerance for standard
        }
      }
      // Targeted mode: only include specified techs
      if (offer.offerMode === 'targeted' && offer.targetTechs.length) {
        if (!offer.targetTechs.includes(tech.id)) return false
      }
      return true
    })
  }

  // ── Create and broadcast an offer ─────────────────────────────────────────
  async function broadcastOffer (offerData, notifyViaSms = false) {
    const pricing = PRICING_PRESETS[offerData.pricingPreset] || PRICING_PRESETS.standard
    const payload = {
      subject: offerData.subject,
      address: offerData.address || '',
      customer: offerData.customer || '',
      scheduled_date: offerData.scheduledDate || '',
      start_time: offerData.startTime || '',
      duration_h: offerData.duration || 1,
      priority: offerData.priority || 'medium',
      status: 'open',
      offer_mode: offerData.offerMode || 'broadcast',
      target_techs: JSON.stringify(offerData.targetTechs || []),
      required_tags: JSON.stringify(offerData.requiredTags || []),
      pricing_preset: offerData.pricingPreset || 'standard',
      displacement_fee: pricing.displacement,
      hourly_rate: pricing.hourlyRate,
      currency: pricing.currency,
      is_customer_request: offerData.isCustomerRequest ? 1 : 0,
      sales_order: offerData.salesOrder || '',
      order_source: offerData.orderSource || 'dispatch',
      expires_at: offerData.expiresAt || '',
      job_name: offerData.jobName || '',
    }
    const doc = await createOffer(payload)
    const mapped = _mapOffer(doc)
    offers.value.unshift(mapped)

    // Notify matching techs via SMS
    if (notifyViaSms) {
      const techs = matchingTechs(mapped)
      const fmtPrice = pricing.displacement > 0
        ? `💰 ${pricing.displacement}$ déplacement + ${pricing.hourlyRate}$/h`
        : ''
      for (const tech of techs) {
        if (!tech.phone) continue
        const msg = [
          `📋 Nouvelle offre de travail:`,
          `${mapped.subject}`,
          mapped.address ? `📍 ${mapped.address}` : '',
          mapped.scheduledDate ? `📅 ${mapped.scheduledDate}${mapped.startTime ? ' à ' + mapped.startTime : ''}` : '',
          `⏱ ${mapped.duration}h`,
          fmtPrice,
          ``,
          `Répondez OUI pour accepter ou NON pour décliner.`,
        ].filter(Boolean).join('\n')
        sendTestSms(tech.phone, msg, mapped.customer, {
          reference_doctype: 'Dispatch Offer',
          reference_name: mapped.id,
        }).catch(err => console.warn(`[offer SMS] ${tech.id}:`, err.message))
      }
    }

    return mapped
  }

  // ── Accept an offer (tech side) ───────────────────────────────────────────
  async function handleAccept (offerId, techId) {
    const offer = offers.value.find(o => o.id === offerId)
    if (!offer) throw new Error('Offer not found')
    // Update offer status
    await acceptOffer(offerId, techId)
    offer.status = 'accepted'
    offer.acceptedBy = techId

    // Create or assign the dispatch job
    if (offer.jobName) {
      // Existing job — assign to accepting tech
      await store.assignJobToTech(offer.jobName, techId,
        store.technicians.find(t => t.id === techId)?.queue.length || 0,
        offer.scheduledDate)
    } else {
      // Create new job from offer
      const job = await store.createJob({
        subject: offer.subject,
        address: offer.address,
        duration_h: offer.duration,
        priority: offer.priority,
        scheduled_date: offer.scheduledDate,
        start_time: offer.startTime,
        assigned_tech: techId,
        customer: offer.customer,
        sales_order: offer.salesOrder,
        order_source: offer.orderSource,
      })
      // Link job back to offer
      if (job?.name) {
        await import('src/api/offers').then(m => m.updateOffer(offerId, { job_name: job.name }))
        offer.jobName = job.name
      }
    }

    return offer
  }

  // ── Decline an offer (tech side) ──────────────────────────────────────────
  async function handleDecline (offerId, techId, reason = '') {
    const offer = offers.value.find(o => o.id === offerId)
    if (!offer) return
    await declineOffer(offerId, techId, reason)
    offer.declinedTechs.push({ techId, reason, at: new Date().toISOString() })
    // Check if all targeted techs declined → auto-expire
    if (offer.offerMode === 'targeted' && offer.targetTechs.length) {
      const allDeclined = offer.targetTechs.every(tid =>
        offer.declinedTechs.some(d => d.techId === tid)
      )
      if (allDeclined) {
        await cancelOffer(offerId)
        offer.status = 'expired'
      }
    }
  }

  // ── Cancel an offer (dispatcher side) ─────────────────────────────────────
  async function handleCancel (offerId) {
    const offer = offers.value.find(o => o.id === offerId)
    if (!offer) return
    await cancelOffer(offerId)
    offer.status = 'cancelled'
  }

  // ── Estimate cost for a rush job ──────────────────────────────────────────
  function estimateCost (preset, durationH) {
    const p = PRICING_PRESETS[preset] || PRICING_PRESETS.standard
    return {
      displacement: p.displacement,
      labour: Math.ceil(durationH * p.hourlyRate * 100) / 100,
      total: p.displacement + Math.ceil(durationH * p.hourlyRate * 100) / 100,
      currency: p.currency,
      description: p.description,
    }
  }

  // ── Create offer from existing unassigned job ─────────────────────────────
  async function offerExistingJob (job, opts = {}) {
    return broadcastOffer({
      jobName: job.name || job.id,
      subject: job.subject,
      address: job.address,
      customer: job.customer,
      scheduledDate: job.scheduledDate,
      startTime: job.startTime,
      duration: job.duration,
      priority: job.priority,
      pricingPreset: opts.pricingPreset || 'standard',
      offerMode: opts.offerMode || 'broadcast',
      targetTechs: opts.targetTechs || [],
      requiredTags: opts.requiredTags || [],
      orderSource: 'dispatch',
      expiresAt: opts.expiresAt || '',
    }, opts.sms !== false)
  }

  return {
    offers, loadingOffers, showOfferPool, activeOfferCount,
    loadOffers, broadcastOffer, handleAccept, handleDecline, handleCancel,
    matchingTechs, estimateCost, offerExistingJob,
  }
}
