#!/usr/bin/python3

"""Grab list of bus from Forgejo and use it to make a line graph."""

import configparser
import json
import os
import sys
import typing
from datetime import date, datetime, timedelta

import pytz
import requests

config = configparser.ConfigParser()
config.read("config")


Bug = dict[str, typing.Any]
CallParams = dict[str, str | int]

hostname = config.get("forgejo", "hostname")
token = config.get("forgejo", "token")
url = f"https://{hostname}/api/v1/repos/issues/search"


def download_bugs(state: str) -> list[Bug]:
    """Get all bugs from forgejo."""
    # params: dict[str, str | int] = {}

    all_bugs: list[Bug] = []
    page = 0
    while bugs := requests.get(
        url,
        headers={"Authorization": "token " + token},
        params=typing.cast(CallParams, {"state": state, "page": page}),
    ).json():
        if isinstance(bugs, dict):
            print(bugs)
            sys.exit(1)
        assert isinstance(bugs, list)
        all_bugs += bugs
        page += 1

    return all_bugs


def get_all_bugs() -> list[Bug]:
    """Get all bugs."""
    filename = "all_bugs.json"
    if os.path.exists(filename):
        with open(filename) as fh:
            return typing.cast(list[Bug], json.load(fh))

    all_bugs = download_bugs("open") + download_bugs("closed")
    with open(filename, "w") as out:
        json.dump(all_bugs, out, indent=2)

    return all_bugs


def parse_date(date_str: str | None) -> date | None:
    """Parse a date string with timezone information."""
    if date_str is None:
        return None

    fmt = "%Y-%m-%dT%H:%M:%SZ" if date_str.endswith("Z") else "%Y-%m-%dT%H:%M:%S%z"
    return datetime.strptime(date_str, fmt).astimezone(pytz.utc).date()


def count_open_bugs(bug_reports: list[Bug]) -> list[tuple[str, int]]:
    """Count the number of open bugs for each date based on a list of bug reports."""
    open_dates: dict[date, int] = {}
    close_dates: dict[date, int] = {}

    # Process each bug report
    seen: set[int] = set()
    for report in bug_reports:
        if report["id"] in seen:
            continue
        seen.add(report["id"])
        open_date = parse_date(report["created_at"])
        if open_date:
            open_dates[open_date] = open_dates.get(open_date, 0) + 1

        close_date = parse_date(report["closed_at"])
        if close_date:
            close_dates[close_date] = close_dates.get(close_date, 0) + 1

    # Create a date range from the earliest open date to the latest close date
    start_date = min(open_dates.keys())
    end_date = max(close_dates.keys(), default=start_date)
    delta = end_date - start_date

    # Count open bugs for each date
    open_bug_count = 0
    open_bugs_over_time = []
    for i in range(delta.days + 1):
        current_date = start_date + timedelta(days=i)
        open_bug_count += open_dates.get(current_date, 0)
        open_bug_count -= close_dates.get(current_date, 0)
        open_bugs_over_time.append((current_date.isoformat(), open_bug_count))

    return open_bugs_over_time


def main() -> None:
    """Grab bug reports and generate chart."""
    bug_reports = get_all_bugs()

    open_bugs_over_time = count_open_bugs(bug_reports)
    json_data = json.dumps(open_bugs_over_time)

    template_html = open("template.html").read()
    with open("chart.html", "w") as out:
        out.write(template_html.replace("jsonData = []", "jsonData = " + json_data))


if __name__ == "__main__":
    main()