import { ref, computed, watch } from 'vue'
import { mockAddressPricing, defaultPricingArea } from 'src/data/pricing-mock'

/**
 * Address-based pricing composable.
 *
 * Responsibilities:
 *   - Load pricing data for a delivery address (stub → mock; later → hub).
 *   - Derive technical fees (distance beyond included + memo extras).
 *   - Sync those fees into orderItems as `address_fee: true` lines so the
 *     cart total already includes them. Lines survive other cart mutations
 *     because the watch re-syncs on every derivedFees change.
 *
 * Contract with other wizard logic:
 *   - address_fee lines are always onetime, never recurring → combo rebate
 *     already ignores them (filters by billing === 'recurring').
 *   - address_fee_id is the stable join key used to add/update/remove.
 */
export function useAddressPricing ({ orderItems }) {
  const pricingData = ref(null)
  const loading = ref(false)
  const currentAddressId = ref(null)

  async function loadAddressPricing (addressId) {
    if (!addressId) {
      resetAddressPricing()
      return
    }
    if (addressId === currentAddressId.value) return
    loading.value = true
    currentAddressId.value = addressId
    try {
      pricingData.value = await mockAddressPricing(addressId)
    } finally {
      loading.value = false
    }
  }

  function resetAddressPricing () {
    pricingData.value = null
    currentAddressId.value = null
    // Purge any lingering address_fee lines so a reopened wizard starts clean.
    const filtered = orderItems.value.filter(i => !i.address_fee)
    if (filtered.length !== orderItems.value.length) {
      orderItems.value = filtered
    }
  }

  // Effective pricing area — real one or fallback defaults.
  const effectiveArea = computed(() => {
    const d = pricingData.value
    if (!d) return null
    return d.pricing_area || defaultPricingArea()
  })

  // Derived fee list. distance-overage is auto-computed; extras are passed
  // through verbatim (the survey values from earlier quotes/visits).
  const derivedFees = computed(() => {
    const d = pricingData.value
    if (!d) return []
    const area = effectiveArea.value
    const fees = []

    const dist = Number(d.distance_to_fiber_m) || 0
    const included = area.default_distance_included_m || 0
    const extra = Math.max(0, dist - included)
    if (extra > 0) {
      const amount = +(extra * (area.fee_per_extra_meter || 0)).toFixed(2)
      fees.push({
        id: 'fee_dist_extra',
        item_code: 'FEE-DIST-EXT',
        item_name: `Extension réseau — ${extra.toFixed(0)} m @ ${area.fee_per_extra_meter.toFixed(2)}$/m`,
        rate: amount,
        billing: 'onetime',
        category: 'distance',
        source: `${dist}m total, ${included}m inclus (${area.name})`,
      })
    }

    for (const e of d.extras || []) {
      fees.push({
        id: `fee_extra_${e.id}`,
        item_code: `FEE-EXT-${String(e.id).slice(-6).toUpperCase()}`,
        item_name: e.label,
        rate: Number(e.amount) || 0,
        billing: 'onetime',
        category: e.category || 'other',
        source: e.note || '',
        extra_id: e.id,
      })
    }

    return fees
  })

  // Reconcile derivedFees ↔ orderItems. This runs idempotently on every
  // derivedFees tick: adds new, updates changed (rate / item_name), removes
  // vanished. Does not touch non-address_fee lines.
  function syncFeesToCart () {
    const derived = derivedFees.value
    const derivedByKey = new Map(derived.map(d => [d.id, d]))

    // Remove orphans first (lines whose derivation no longer exists).
    for (let i = orderItems.value.length - 1; i >= 0; i--) {
      const line = orderItems.value[i]
      if (!line.address_fee) continue
      if (!derivedByKey.has(line.address_fee_id)) {
        orderItems.value.splice(i, 1)
      }
    }

    // Add/update.
    const existingByKey = new Map()
    for (const line of orderItems.value) {
      if (line.address_fee) existingByKey.set(line.address_fee_id, line)
    }
    for (const d of derived) {
      const ex = existingByKey.get(d.id)
      if (!ex) {
        orderItems.value.push({
          address_fee: true,
          address_fee_id: d.id,
          item_code: d.item_code,
          item_name: d.item_name,
          qty: 1,
          rate: d.rate,
          regular_price: 0,
          billing: 'onetime',
          billing_interval: 'Month',
          contract_months: 12,
          project_template_id: '',
          fee_category: d.category,
          fee_source: d.source,
        })
      } else {
        if (ex.rate !== d.rate) ex.rate = d.rate
        if (ex.item_name !== d.item_name) ex.item_name = d.item_name
        if (ex.item_code !== d.item_code) ex.item_code = d.item_code
        if (ex.fee_source !== d.source) ex.fee_source = d.source
      }
    }
  }

  watch(derivedFees, syncFeesToCart, { deep: true, immediate: true })

  function updateDistance (meters) {
    if (!pricingData.value) return
    pricingData.value = { ...pricingData.value, distance_to_fiber_m: Number(meters) || 0 }
  }

  function addExtra (extra) {
    if (!pricingData.value) return
    const id = extra.id || `ex_${Date.now()}_${Math.random().toString(36).slice(2, 7)}`
    pricingData.value = {
      ...pricingData.value,
      extras: [...(pricingData.value.extras || []), {
        id,
        label: extra.label,
        category: extra.category || 'other',
        amount: Number(extra.amount) || 0,
        note: extra.note || '',
      }],
    }
  }

  function removeExtra (extraId) {
    if (!pricingData.value) return
    pricingData.value = {
      ...pricingData.value,
      extras: (pricingData.value.extras || []).filter(e => e.id !== extraId),
    }
  }

  const totalFees = computed(() =>
    derivedFees.value.reduce((sum, f) => sum + (f.rate || 0), 0)
  )

  return {
    pricingData,
    loading,
    effectiveArea,
    derivedFees,
    totalFees,
    loadAddressPricing,
    resetAddressPricing,
    updateDistance,
    addExtra,
    removeExtra,
  }
}
