import { promises as fs } from 'fs'
import path from 'path'
import { getServerDataPath } from './paths.js'

const DEFAULT_CONFIG = {
  association: 'HeTTV',
  clubId: '43030',
  clubName: 'Harheimer_TC'
}

const OUTPUT_DIR = getServerDataPath('spielplan-import')
const JSON_FILE = path.join(OUTPUT_DIR, 'harheimer_tc_spielplan.json')
const HTML_FILE = path.join(OUTPUT_DIR, 'harheimer_tc_spielplan.html')

function pad2(value) {
  return String(value).padStart(2, '0')
}

export function getSpieljahrForDate(date = new Date()) {
  const year = date.getFullYear()
  const startYear = date.getMonth() >= 6 ? year : year - 1
  const endYear = startYear + 1

  return {
    startYear,
    endYear,
    seasonSlug: `${String(startYear).slice(-2)}--${String(endYear).slice(-2)}`,
    dateStart: `${startYear}-07-01`,
    dateEnd: `${endYear}-06-30`
  }
}

export function buildSpielplanUrl(season, config = DEFAULT_CONFIG) {
  const base = `https://www.mytischtennis.de/click-tt/${config.association}/${season.seasonSlug}/verein/${config.clubId}/${config.clubName}/spielplan`
  return `${base}?date_start=${season.dateStart}&date_end=${season.dateEnd}`
}

function extractRemixContext(html) {
  const marker = 'window.__remixContext = '
  const start = html.indexOf(marker)
  if (start === -1) {
    throw new Error('window.__remixContext nicht gefunden')
  }

  const jsonStart = start + marker.length
  let depth = 0
  let inString = false
  let escaped = false

  for (let i = jsonStart; i < html.length; i += 1) {
    const char = html[i]

    if (inString) {
      if (escaped) {
        escaped = false
      } else if (char === '\\') {
        escaped = true
      } else if (char === '"') {
        inString = false
      }
      continue
    }

    if (char === '"') {
      inString = true
    } else if (char === '{') {
      depth += 1
    } else if (char === '}') {
      depth -= 1
      if (depth === 0) {
        return JSON.parse(html.slice(jsonStart, i + 1))
      }
    }
  }

  throw new Error('Ende von window.__remixContext nicht gefunden')
}

function looksLikeScheduleByDate(value) {
  if (!value || typeof value !== 'object' || Array.isArray(value)) {
    return false
  }

  return Object.entries(value).some(([key, list]) => (
    /^\d{4}-\d{2}-\d{2}$/.test(key) &&
    Array.isArray(list) &&
    list.some((item) => item && item.team_home && item.team_away && item.meeting_id)
  ))
}

function findSchedule(value, trail = []) {
  if (looksLikeScheduleByDate(value)) {
    return { schedule: value, path: trail }
  }

  if (!value || typeof value !== 'object' || Array.isArray(value)) {
    return null
  }

  for (const [key, child] of Object.entries(value)) {
    const result = findSchedule(child, trail.concat(key))
    if (result) return result
  }

  return null
}

function normalizeMatch(day, match) {
  const dateTimestamp = match.date ? Date.parse(match.date) : NaN

  return {
    day,
    date: match.date ?? null,
    timestamp: Number.isNaN(dateTimestamp) ? null : Math.floor(dateTimestamp / 1000),
    formattedDay: match.formattedDay ?? null,
    formattedTime: match.formattedTime ?? null,
    state: match.state ?? null,
    meetingId: match.meeting_id ?? null,
    meetingNumber: match.meeting_number ?? null,
    leagueId: match.league_id ?? null,
    leagueName: match.league_name ?? null,
    leagueShortName: match.league_short_name ?? null,
    leagueOrgShortName: match.league_org_short_name ?? null,
    roundName: match.round_name ?? null,
    teamHome: match.team_home ?? null,
    teamHomeId: match.team_home_id ?? null,
    teamHomeClubId: match.team_home_club_id ?? null,
    teamAway: match.team_away ?? null,
    teamAwayId: match.team_away_id ?? null,
    teamAwayClubId: match.team_away_club_id ?? null,
    result: match.matches_won != null && match.matches_lost != null
      ? `${match.matches_won}:${match.matches_lost}`
      : null,
    isConfirmed: match.is_confirmed ?? null,
    isComplete: match.is_meeting_complete ?? null,
    originalDate: match.original_date ?? null,
    location: match.location ?? null,
    pdfUrl: match.pdf_url ?? null
  }
}

export function parseSpielplanHtml(html, source) {
  const context = extractRemixContext(html)
  const result = findSchedule(context.state?.loaderData)
  if (!result) {
    throw new Error('Keinen Spielplan im Remix loaderData gefunden')
  }

  const matchesByDay = result.schedule
  const matches = Object.keys(matchesByDay)
    .sort()
    .flatMap((day) => matchesByDay[day].map((match) => normalizeMatch(day, match)))

  return {
    importedAt: new Date().toISOString(),
    source,
    loaderDataPath: result.path.join('.'),
    matchCount: matches.length,
    matchesByDay,
    matches
  }
}

export async function importSpielplan(options = {}) {
  const today = options.today ?? new Date()
  const season = getSpieljahrForDate(today)
  const url = buildSpielplanUrl(season)
  const response = await fetch(url, {
    headers: {
      'accept': 'text/html,application/xhtml+xml',
      'accept-language': 'de-DE,de;q=0.9'
    }
  })

  if (!response.ok) {
    throw new Error(`Spielplan-Download fehlgeschlagen: HTTP ${response.status}`)
  }

  const html = await response.text()
  const parsed = parseSpielplanHtml(html, {
    url,
    clubId: DEFAULT_CONFIG.clubId,
    clubName: DEFAULT_CONFIG.clubName,
    association: DEFAULT_CONFIG.association,
    season
  })

  await fs.mkdir(OUTPUT_DIR, { recursive: true })
  await fs.writeFile(HTML_FILE, html, 'utf8')
  await fs.writeFile(JSON_FILE, `${JSON.stringify(parsed, null, 2)}\n`, 'utf8')

  return {
    jsonFile: JSON_FILE,
    htmlFile: HTML_FILE,
    ...parsed
  }
}

export async function readImportedSpielplan() {
  const content = await fs.readFile(JSON_FILE, 'utf8')
  return JSON.parse(content)
}
