import urllib.request
import re
import urllib.parse
import json

base_url = "https://open.kattis.com"
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko)'}

contest_mapping_rules = {
    "acpc": (["Arab Collegiate", "ACPC", "Jordanian", "Lebanese", "Tunisian"], "Arab Collegiate Programming Contest"),
    "bapc": (["Benelux Algorithm Programming Contest (BAPC)", "BAPC 20"], "Benelux Algorithm Programming Contest (BAPC)"),
    "bapc-prelim": (["preliminaries", "prelim", "prelims"], "Benelux Algorithm Programming Championship Preliminaries (BAPC Prelims)"),
    "cerc": (["Central Europe Regional Contest", "CERC"], "Central Europe Regional Contest"),
    "ecna": (["East-Central NA", "East Central North America", "ECNA"], "East Central North America Regional Contest"),
    "finals": (["World Finals"], "ICPC World Finals"),
    "gcpc": (["German Collegiate Programming Contest", "GCPC"], "German Collegiate Programming Championship"),
    "gnyr": (["Greater New York"], "Greater New York Regional Contest"),
    "mcpc": (["Mid-Central", "MCPC"], "Mid-Central USA Regional Contest"),
    "midatl": (["Mid-Atlantic"], "North American Mid-Atlantic Region"),
    "nane": (["Northeast North America", "NANE", "NENA"], "Northeast North America Regional Contest"),
    "nac": (["North America Championship", "NAC 20", "NAC 21"], "North America Championship"),
    "nadc": (["Division Championships", "NADC"], "North America Division Championship"),
    # UCIPC / University of Chicago Invitational is naipc!
    "naipc": (["North American Invitational", "NAIPC", "UCIPC", "Chicago Invitational"], "North American Invitational Programming Contest"),
    "naq": (["North American Qualifier", "North America Qualifier", "NAQ"], "North America Qualifier"),
    "ncna": (["North Central"], "North Central North America Regional Contest"),
    "ncpc": (["Nordic Collegiate Programming Contest", "NCPC"], "Nordic Collegiate Programming Contest"),
    "nwerc": (["Northwestern Europe", "NWERC"], "Northwestern Europe Regional Contest"),
    "pacnw": (["Pacific Northwest", "PacNW"], "Pacific Northwest Regional Contest"),
    "rmrc": (["Rocky Mountain", "RMRC"], "Rocky Mountain Regional Contest"),
    "scusa": (["South Central"], "South Central USA Regional Contest"),
    "seerc": (["Southeastern Europe", "SEERC"], "Southeastern Europe Regional Contest"),
    "ser": (["Southeast USA", "SER "], "Southeast USA Regional Contest"),
    "socal": (["Southern California", "SoCal"], "Southern California Regional Contest"),
    "swerc": (["Southwestern Europe", "SWERC"], "Southwestern Europe Regional Contest"),
    "ukiepc": (["UK & Ireland", "UKIEPC"], "United Kingdom & Ireland Evening Programming Contest"),
    "amppz": (["AMPPZ", "Poland Collegiate"], "Polish Collegiate Programming Contest (AMPPZ)"),
    "ctuopen": (["CTU Open"], "Czech Technical University Open (CTU Open)"),
    "hcpc": (["HCPC", "Hungarian Collegiate"], "Hungarian Collegiate Programming Contest (HCPC)"),
    "adabyron": (["AdaByron"], "Spanish AdaByron Collegiate Programming Contest"),
    "itacpc": (["ITACPC", "Italian Collegiate"], "Italian Collegiate Programming Contest (ITACPC)"),
    "miup": (["MIUP", "Portuguese Inter-University"], "Portuguese Inter-University Programming Marathon (MIUP)"),
    "grcpc": (["GRCPC", "Greek Collegiate"], "Greek Collegiate Programming Contest (GRCPC)"),
    "ukr": (["Ukrainian Collegiate", "UKR"], "Ukrainian Collegiate Programming Contest (UKR)"),
    "trcpc": (["TRCPC", "Turkish Collegiate"], "Turkish Collegiate Programming Contest (TRCPC)")
}

# Explicit associations of shared regional sets (crawled and aligned 100% correctly)
shared_regional_alignments = {
    "2020 ICPC North America Regionals (March 6 2021)": [
        ("North American Mid-Atlantic Region", "midatl"),
        ("Southeast USA Regional Contest", "ser"),
        ("South Central USA Regional Contest", "scusa")
    ],
    "2021 ICPC North America Regionals (February 26 2022)": [
        ("North American Mid-Atlantic Region", "midatl"),
        ("Southeast USA Regional Contest", "ser"),
        ("South Central USA Regional Contest", "scusa")
    ],
    "2021 ICPC North America Regional Programming Contests (March 5 2022)": [
        ("North American Mid-Atlantic Region", "midatl"),
        ("Southeast USA Regional Contest", "ser"),
        ("South Central USA Regional Contest", "scusa")
    ],
    "2021 ICPC North America Regionals Practice (March 5 2022)": [
        ("North American Mid-Atlantic Region", "midatl"),
        ("Southeast USA Regional Contest", "ser"),
        ("South Central USA Regional Contest", "scusa")
    ],
    "2022 ICPC North America Regional Contests (February 25, 2023)": [
        ("North American Mid-Atlantic Region", "midatl"),
        ("Southeast USA Regional Contest", "ser"),
        ("South Central USA Regional Contest", "scusa"),
        ("East Central North America Regional Contest", "ecna"),
        ("Greater New York Regional Contest", "gnyr")
    ],
    "2022 ICPC North America Regional Programming Contests (February 25, 2023)": [
        ("North American Mid-Atlantic Region", "midatl"),
        ("Southeast USA Regional Contest", "ser"),
        ("South Central USA Regional Contest", "scusa"),
        ("East Central North America Regional Contest", "ecna"),
        ("Greater New York Regional Contest", "gnyr")
    ],
    "2023 ICPC North America Regional Programming Contests (February 24, 2024)": [
        ("North American Mid-Atlantic Region", "midatl"),
        ("Southeast USA Regional Contest", "ser"),
        ("South Central USA Regional Contest", "scusa")
    ],
    "2024 ICPC South Central, South East and Mid Atlantic Regional (November 16, 2024)": [
        ("North American Mid-Atlantic Region", "midatl"),
        ("Southeast USA Regional Contest", "ser"),
        ("South Central USA Regional Contest", "scusa")
    ],
    "2025 ICPC South Central, South East and Mid Atlantic Regional (November 8, 2025)": [
        ("North American Mid-Atlantic Region", "midatl"),
        ("Southeast USA Regional Contest", "ser"),
        ("South Central USA Regional Contest", "scusa")
    ],
    "ICPC North America Regional Contests 2019-11-09": [
        ("North American Mid-Atlantic Region", "midatl"),
        ("Southeast USA Regional Contest", "ser"),
        ("South Central USA Regional Contest", "scusa")
    ],
    "ICPC NCNA Regionals 2025 2026": [
        ("North Central North America Regional Contest", "ncna")
    ],
    
    # Division-specific multi-regional sets
    "2023 North America East Division Contest": [
        ("Northeast North America Regional Contest", "nane"),
        ("Greater New York Regional Contest", "gnyr"),
        ("East Central North America Regional Contest", "ecna")
    ],
    "2024 North America East Division Contest": [
        ("Northeast North America Regional Contest", "nane"),
        ("Greater New York Regional Contest", "gnyr"),
        ("East Central North America Regional Contest", "ecna")
    ],
    "2025 North America East Division Contest": [
        ("Northeast North America Regional Contest", "nane"),
        ("Greater New York Regional Contest", "gnyr"),
        ("East Central North America Regional Contest", "ecna")
    ],
    "2025 ICPC North America Central Division": [
        ("Mid-Central USA Regional Contest", "mcpc"),
        ("North Central North America Regional Contest", "ncna")
    ]
}

def generate_report():
    sources = []
    print("Scraping open.kattis.com/problem-sources to compile the complete list...")
    for page in range(1, 11):
        url = f"{base_url}/problem-sources?page={page}"
        try:
            req = urllib.request.Request(url, headers=headers)
            with urllib.request.urlopen(req, timeout=10) as response:
                html = response.read().decode('utf-8')
                matches = re.findall(r'href="/problem-sources/([^"]+)"', html)
                for m in matches:
                    decoded = urllib.parse.unquote(m)
                    sources.append(decoded)
        except Exception as e:
            print(f"Error crawling page {page}: {e}")
            break
            
    unique_sources = sorted(list(set(sources)))
    print(f"Scraped {len(unique_sources)} total unique Kattis problem sources.")
    
    aligned = []
    unaligned = []
    
    for s in unique_sources:
        matched = False
        aligned_contests_list = [] # Supports multiple aligned contests!
        
        # Check if this source is in our explicitly resolved shared regional maps
        if s in shared_regional_alignments:
            matched = True
            for full_name, acronym in shared_regional_alignments[s]:
                aligned_contests_list.append((acronym, full_name))
        else:
            # Test against standard mapping rules
            for acronym, (keywords, full_name) in contest_mapping_rules.items():
                if acronym == "bapc" and "prelim" in s.lower():
                    continue
                if acronym == "bapc-prelim" and "prelim" not in s.lower():
                    continue
                    
                for kw in keywords:
                    if kw.lower() in s.lower():
                        matched = True
                        aligned_contests_list.append((acronym, full_name))
                        break
                if matched:
                    break
                        
        if matched:
            aligned.append((s, aligned_contests_list))
        else:
            unaligned.append(s)
            
    # Write the report
    report_lines = []
    report_lines.append("# Kattis Problem Sources Alignment Report")
    report_lines.append("")
    report_lines.append("This report documents all **Kattis problem sources** scraped from the live `open.kattis.com/problem-sources` index, classifying them into those successfully aligned to our ICPC contests and those unable to align.")
    report_lines.append("")
    report_lines.append(f"**Total Scraped Sources:** {len(unique_sources)}")
    report_lines.append(f"*   **Aligned Sources:** {len(aligned)}")
    report_lines.append(f"*   **Unaligned Sources:** {len(unaligned)}")
    report_lines.append("")
    
    report_lines.append("## 🔗 Successfully Aligned Kattis Problem Sources")
    report_lines.append("")
    report_lines.append("The following Kattis problem sources correspond directly to our tracked ICPC contests, including shared regional problem sets used across multiple regions:")
    report_lines.append("")
    report_lines.append("| Kattis Problem Source Name | Aligned Contest(s) | Directory Acronym(s) | Live URL |")
    report_lines.append("|---|---|---|---|")
    
    # We pad cells for beautiful alignment
    table_rows = []
    for s, contests in aligned:
        escaped_src = urllib.parse.quote(s)
        kattis_url = f"[Open.Kattis](https://open.kattis.com/problem-sources/{escaped_src})"
        
        # Combine multiple aligned contests nicely
        c_names = " <br> ".join([info[1] for info in contests])
        c_acronyms = " <br> ".join([f"`{info[0]}`" for info in contests])
        table_rows.append([s, c_names, c_acronyms, kattis_url])
        
    # Formatting aligned table
    col_widths = [len(h) for h in ["Kattis Problem Source Name", "Aligned Contest(s)", "Directory Acronym(s)", "Live URL"]]
    for row in table_rows:
        for i in range(4):
            col_widths[i] = max(col_widths[i], len(row[i]))
            
    header_line = "| " + " | ".join(h.ljust(col_widths[i]) for i, h in enumerate(["Kattis Problem Source Name", "Aligned Contest(s)", "Directory Acronym(s)", "Live URL"])) + " |"
    sep_line = "|-" + "-|-".join("-" * col_widths[i] for i in range(4)) + "-|"
    report_lines.append(header_line)
    report_lines.append(sep_line)
    for row in table_rows:
        report_lines.append("| " + " | ".join(row[i].ljust(col_widths[i]) for i in range(4)) + " |")
        
    report_lines.append("")
    report_lines.append("## ❌ Unable to Align Kattis Problem Sources")
    report_lines.append("")
    report_lines.append("The following Kattis problem sources do not correspond to any of our tracked active ICPC contests (representing national olympiads, school programming classes, corporate contests, etc.):")
    report_lines.append("")
    
    for s in unaligned:
        escaped_src = urllib.parse.quote(s)
        report_lines.append(f"*   [{s}](https://open.kattis.com/problem-sources/{escaped_src})")
        
    report_path = "/run/media/kevin/web/icpc/data/kattis_sources.md"
    with open(report_path, "w", encoding="utf-8") as rf:
        rf.write("\n".join(report_lines))
    print(f"Saved Kattis sources report to {report_path}!")

if __name__ == "__main__":
    generate_report()
