#!/usr/bin/env node
// Parses src/lib/tapestry/window.ts for the PAGE_MINUTES constant and
// emits a Swift implementation of the same UTC-bucket math under
// targets/watch/Generated/TapestryWindow.swift.
//
// The math itself is reimplemented in Swift (not parsed) — it's small
// enough that a hand-rolled mirror is clearer than a TS-to-Swift
// transpile. The single thing that MUST stay in sync is PAGE_MINUTES,
// which we extract and inline.

import { mkdirSync, readFileSync, writeFileSync } from 'node:fs'
import { dirname, join } from 'node:path'
import { fileURLToPath } from 'node:url'

const HERE = dirname(fileURLToPath(import.meta.url))
const REPO_ROOT = join(HERE, '..', '..')
const WINDOW_TS = join(REPO_ROOT, 'src', 'lib', 'tapestry', 'window.ts')
const OUT = join(REPO_ROOT, 'targets', 'watch', 'Generated', 'TapestryWindow.swift')

function parsePageMinutes(src) {
  const m = src.match(/export\s+const\s+PAGE_MINUTES\s*=\s*(\d+)/)
  if (!m) throw new Error('gen-tapestry-window: PAGE_MINUTES not found')
  const n = Number(m[1])
  if (!Number.isFinite(n) || n <= 0) {
    throw new Error(`gen-tapestry-window: invalid PAGE_MINUTES ${m[1]}`)
  }
  return n
}

export function generate() {
  const src = readFileSync(WINDOW_TS, 'utf8')
  const pageMinutes = parsePageMinutes(src)
  const swift = `// AUTO-GENERATED — do not edit. Source: src/lib/tapestry/window.ts
// Regenerate via \`node targets/_scripts/gen-tapestry-window.mjs\` (runs at
// prebuild time via plugins/withWatchGenerated.js).
//
// PAGE_MINUTES is mirrored from window.ts; the math is reimplemented in
// Swift. The two implementations must produce identical bucket strings
// for the same Date input — see __tests__/tapestryWindowParity.swift if
// it exists, or eyeball-verify against the TS unit tests.

import Foundation

enum TapestryWindow {
    static let pageMinutes = ${pageMinutes}

    private static let utc: TimeZone = TimeZone(identifier: "UTC")!
    private static let calendar: Calendar = {
        var c = Calendar(identifier: .gregorian)
        c.timeZone = utc
        return c
    }()

    static func formatBucket(_ date: Date) -> String {
        let comps = calendar.dateComponents(
            [.year, .month, .day, .hour, .minute],
            from: date
        )
        return String(
            format: "%04d%02d%02d%02d%02d",
            comps.year ?? 0,
            comps.month ?? 0,
            comps.day ?? 0,
            comps.hour ?? 0,
            comps.minute ?? 0
        )
    }

    static func parseBucket(_ value: String) -> Date? {
        guard value.count == 12, value.allSatisfy({ $0.isASCII && $0.isNumber }) else {
            return nil
        }
        let s = Array(value)
        guard
            let year = Int(String(s[0..<4])),
            let month = Int(String(s[4..<6])),
            let day = Int(String(s[6..<8])),
            let hour = Int(String(s[8..<10])),
            let minute = Int(String(s[10..<12]))
        else { return nil }
        var comps = DateComponents()
        comps.timeZone = utc
        comps.year = year
        comps.month = month
        comps.day = day
        comps.hour = hour
        comps.minute = minute
        comps.second = 0
        guard let d = calendar.date(from: comps) else { return nil }
        // Strict round-trip: reject silent rollovers (month=13, day=32, …).
        if formatBucket(d) != value { return nil }
        return d
    }

    static func currentBucket(_ now: Date = Date()) -> String {
        let comps = calendar.dateComponents(
            [.year, .month, .day, .hour, .minute],
            from: now
        )
        var floored = DateComponents()
        floored.timeZone = utc
        floored.year = comps.year
        floored.month = comps.month
        floored.day = comps.day
        floored.hour = comps.hour
        floored.minute = comps.minute
        floored.second = 0
        return formatBucket(calendar.date(from: floored) ?? now)
    }

    static func currentPage(_ now: Date = Date()) -> String {
        let comps = calendar.dateComponents(
            [.year, .month, .day, .hour, .minute],
            from: now
        )
        let m = (comps.minute ?? 0) - ((comps.minute ?? 0) % pageMinutes)
        var floored = DateComponents()
        floored.timeZone = utc
        floored.year = comps.year
        floored.month = comps.month
        floored.day = comps.day
        floored.hour = comps.hour
        floored.minute = m
        floored.second = 0
        return formatBucket(calendar.date(from: floored) ?? now)
    }

    static func previousPage(_ page: String) -> String? {
        guard let d = parseBucket(page) else { return nil }
        return formatBucket(d.addingTimeInterval(TimeInterval(-pageMinutes * 60)))
    }

    static func nextPage(_ page: String) -> String? {
        guard let d = parseBucket(page) else { return nil }
        return formatBucket(d.addingTimeInterval(TimeInterval(pageMinutes * 60)))
    }
}
`
  mkdirSync(dirname(OUT), { recursive: true })
  writeFileSync(OUT, swift)
  return { path: OUT, pageMinutes }
}

if (import.meta.url === `file://${process.argv[1]}`) {
  const { path, pageMinutes } = generate()
  console.log(`gen-tapestry-window: wrote ${path} (PAGE_MINUTES=${pageMinutes})`)
}
