'use strict'
const crypto = require('crypto')
const cfg = require('./config')
const { log, json } = require('./helpers')
const erp = require('./erp')

const ICAL_SECRET = cfg.ICAL_SECRET || cfg.INTERNAL_TOKEN || 'gigafibre-ical-2026'

function generateToken (techId) {
  return crypto.createHmac('sha256', ICAL_SECRET).update(techId).digest('hex').slice(0, 16)
}

function validateToken (techId, token) {
  return token === generateToken(techId)
}

function icalDate (dateStr, timeStr) {
  const [y, m, d] = dateStr.split('-')
  if (!timeStr) return `${y}${m}${d}`
  const [hh, mm] = timeStr.split(':')
  return `${y}${m}${d}T${hh}${mm || '00'}00`
}

function icalNow () {
  const d = new Date()
  return d.toISOString().replace(/[-:]/g, '').replace(/\.\d+Z/, 'Z')
}

// Escape iCal text values (fold long lines handled by client)
function esc (s) {
  if (!s) return ''
  return s.replace(/\\/g, '\\\\').replace(/;/g, '\\;').replace(/,/g, '\\,').replace(/\n/g, '\\n')
}

function buildICal (techName, jobs, techId) {
  const lines = [
    'BEGIN:VCALENDAR',
    'VERSION:2.0',
    'PRODID:-//Gigafibre//Dispatch//FR',
    'CALSCALE:GREGORIAN',
    'METHOD:PUBLISH',
    `X-WR-CALNAME:${esc(techName)} — Dispatch`,
    'X-WR-TIMEZONE:America/Toronto',
    // Timezone definition for EST/EDT
    'BEGIN:VTIMEZONE',
    'TZID:America/Toronto',
    'BEGIN:DAYLIGHT',
    'DTSTART:19700308T020000',
    'RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU',
    'TZOFFSETFROM:-0500',
    'TZOFFSETTO:-0400',
    'TZNAME:EDT',
    'END:DAYLIGHT',
    'BEGIN:STANDARD',
    'DTSTART:19701101T020000',
    'RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU',
    'TZOFFSETFROM:-0400',
    'TZOFFSETTO:-0500',
    'TZNAME:EST',
    'END:STANDARD',
    'END:VTIMEZONE',
  ]

  for (const job of jobs) {
    if (!job.scheduled_date) continue
    const startTime = job.start_time || '08:00'
    const dur = parseFloat(job.duration_h) || 1
    const endMin = Math.round((parseFloat(startTime.split(':')[0]) + parseFloat(startTime.split(':')[1] || 0) / 60 + dur) * 60)
    const endHH = String(Math.floor(endMin / 60)).padStart(2, '0')
    const endMM = String(endMin % 60).padStart(2, '0')
    const endTime = `${endHH}:${endMM}`

    const dtStart = icalDate(job.scheduled_date, startTime)
    const dtEnd = icalDate(job.scheduled_date, endTime)
    const uid = `${job.name || job.ticket_id}@dispatch.gigafibre.ca`

    const summary = job.subject || 'Dispatch Job'
    const location = job.address || ''
    const desc = [
      job.customer ? `Client: ${job.customer}` : '',
      job.priority ? `Priorité: ${job.priority}` : '',
      `Durée: ${dur}h`,
      job.status ? `Statut: ${job.status}` : '',
      job.notes ? `\nNotes: ${job.notes}` : '',
    ].filter(Boolean).join('\\n')

    // Status mapping
    let icalStatus = 'CONFIRMED'
    const st = (job.status || '').toLowerCase()
    if (st === 'open') icalStatus = 'TENTATIVE'
    if (st === 'cancelled') icalStatus = 'CANCELLED'
    if (st === 'completed') icalStatus = 'CANCELLED' // don't show completed

    lines.push(
      'BEGIN:VEVENT',
      `UID:${uid}`,
      `DTSTAMP:${icalNow()}`,
      `DTSTART;TZID=America/Toronto:${dtStart}`,
      `DTEND;TZID=America/Toronto:${dtEnd}`,
      `SUMMARY:${esc(summary)}`,
      location ? `LOCATION:${esc(location)}` : null,
      `DESCRIPTION:${desc}`,
      `STATUS:${icalStatus}`,
      // Recurrence rule (if recurring template)
      job.is_recurring && job.recurrence_rule ? `RRULE:${job.recurrence_rule}${job.recurrence_end ? ';UNTIL=' + job.recurrence_end.replace(/-/g, '') + 'T235959' : ''}` : null,
      // Color hint (non-standard but supported by some clients)
      job.priority === 'high' ? 'PRIORITY:1' : job.priority === 'medium' ? 'PRIORITY:5' : 'PRIORITY:9',
      'END:VEVENT',
    )
  }

  lines.push('END:VCALENDAR')
  return lines.filter(Boolean).join('\r\n')
}

async function handleCalendar (req, res, techId, query) {
  try {
    const token = query?.get?.('token') || ''
    if (!validateToken(techId, token)) {
      return json(res, 403, { error: 'Invalid or missing token. Get the link from the dispatch app.' })
    }

    // Fetch tech info
    const techs = await erp.list('Dispatch Technician', {
      fields: ['name', 'technician_id', 'full_name'],
      filters: [['technician_id', '=', techId]],
      limit: 1,
    })
    if (!techs.length) return json(res, 404, { error: 'Tech not found' })
    const tech = techs[0]

    // Fetch jobs for this tech: past 7 days + future 60 days
    const now = new Date()
    const past = new Date(now); past.setDate(past.getDate() - 7)
    const future = new Date(now); future.setDate(future.getDate() + 60)
    const fromStr = past.toISOString().slice(0, 10)
    const toStr = future.toISOString().slice(0, 10)

    const jobs = await erp.list('Dispatch Job', {
      fields: [
        'name', 'ticket_id', 'subject', 'address', 'scheduled_date', 'start_time',
        'duration_h', 'priority', 'status', 'customer', 'notes',
        'is_recurring', 'recurrence_rule', 'recurrence_end',
      ],
      filters: [
        ['assigned_tech', '=', techId],
        ['scheduled_date', '>=', fromStr],
        ['scheduled_date', '<=', toStr],
        ['status', '!=', 'cancelled'],
      ],
      orderBy: 'scheduled_date asc, start_time asc',
      limit: 500,
    })
    const ical = buildICal(tech.full_name, jobs, techId)

    res.writeHead(200, {
      'Content-Type': 'text/calendar; charset=utf-8',
      'Content-Disposition': `inline; filename="${techId}.ics"`,
      'Cache-Control': 'no-cache, max-age=0',
    })
    res.end(ical)
  } catch (e) {
    log('iCal error:', e.message)
    json(res, 500, { error: e.message })
  }
}

module.exports = { handleCalendar, buildICal, generateToken }
