#!/usr/bin/env node

import fs from "node:fs";
import path from "node:path";
import { execFileSync } from "node:child_process";
import { fileURLToPath } from "node:url";

const __dirname = path.dirname(fileURLToPath(import.meta.url));

const CONFIG = {
  association: "HeTTV",
  clubId: "43030",
  clubName: "Harheimer_TC",
  outputDir: path.join(__dirname, "data"),
  outputFile: "harheimer_tc_spielplan.json",
  htmlFile: "harheimer_tc_spielplan.html",
};

function parseArgs(argv) {
  const args = {};
  for (let i = 2; i < argv.length; i += 1) {
    const arg = argv[i];
    if (arg.startsWith("--")) {
      const key = arg.slice(2);
      const next = argv[i + 1];
      if (!next || next.startsWith("--")) {
        args[key] = true;
      } else {
        args[key] = next;
        i += 1;
      }
    }
  }
  return args;
}

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

function seasonForDate(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`,
  };
}

function buildUrl(season) {
  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 readHtml(args, url) {
  if (args.input) {
    return fs.readFileSync(path.resolve(args.input), "utf8");
  }

  const html = execFileSync("curl", ["-fsSL", "--compressed", url], {
    encoding: "utf8",
    maxBuffer: 20 * 1024 * 1024,
  });

  fs.mkdirSync(CONFIG.outputDir, { recursive: true });
  fs.writeFileSync(path.join(CONFIG.outputDir, CONFIG.htmlFile), html);
  return html;
}

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;
  }

  const entries = Object.entries(value);
  if (entries.length === 0) {
    return false;
  }

  return entries.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") {
    return null;
  }

  if (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) {
  return {
    day,
    date: match.date ?? null,
    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,
  };
}

function parseSchedule(html, meta) {
  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: meta,
    loaderDataPath: result.path.join("."),
    matchCount: matches.length,
    matchesByDay,
    matches,
  };
}

function main() {
  const args = parseArgs(process.argv);
  const today = args.today ? new Date(`${args.today}T12:00:00`) : new Date();
  if (Number.isNaN(today.getTime())) {
    throw new Error(`Ungueltiges Datum fuer --today: ${args.today}`);
  }

  const season = seasonForDate(today);
  const url = buildUrl(season);
  const html = readHtml(args, url);
  const parsed = parseSchedule(html, {
    url,
    clubId: CONFIG.clubId,
    clubName: CONFIG.clubName,
    association: CONFIG.association,
    season,
  });

  fs.mkdirSync(CONFIG.outputDir, { recursive: true });
  const outputPath = path.join(CONFIG.outputDir, CONFIG.outputFile);
  fs.writeFileSync(outputPath, `${JSON.stringify(parsed, null, 2)}\n`);

  console.log(`Spielplan gespeichert: ${outputPath}`);
  console.log(`Spiele: ${parsed.matchCount}`);
  console.log(`Zeitraum: ${season.dateStart} bis ${season.dateEnd}`);
}

main();
