"""
.dproj Fixed Code Comparison Test

Compares the FIXED _handle_create output against the previous run's issues.
Tests 5 project types with Win32+Win64 platforms, Debug+Release configs.

Usage: python tests/test_dproj_comparison_fixed.py
Output: tests/test_dproj_comparison_fixed.md
"""

import asyncio
import sys
import tempfile
from pathlib import Path
import xml.etree.ElementTree as ET

sys.path.insert(0, str(Path(__file__).resolve().parent.parent))

from src.tools.dproj_tool import _handle_create
from src.utils.dproj_parser import DprojParser

MSBUILD_NS = "http://schemas.microsoft.com/developer/msbuild/2003"


def _tag(name):
    return f"{{{MSBUILD_NS}}}{name}"


async def run():
    report_lines = []
    out = report_lines.append

    out("# .dproj Fixed Code Comparison Report")
    out("")
    out("**Generator**: `_handle_create` from `src.tools.dproj_tool`")
    out("**Parser**: `DprojParser` from `src.utils.dproj_parser`")
    out("**Platforms**: `[\"Win32\", \"Win64\"]` | **Configs**: `[\"Debug\", \"Release\"]`")
    out("")

    projects = [
        ("VCL App",    "VCL",  "Application"),
        ("FMX App",    "FMX",  "Application"),
        ("Console App","None", "Console"),
        ("Package",    "VCL",  "Package"),
        ("Library",    "None", "Library"),
    ]

    master_ok = {}
    master_fail = {}

    with tempfile.TemporaryDirectory() as tmpdir:
        for ptype, framework, app_type in projects:
            proj_name = ptype.replace(" ", "")
            proj_path = Path(tmpdir) / f"{proj_name}.dproj"
            main_src = f"{proj_name}.dpr"

            out(f"{'=' * 80}")
            out(f"## Project Type: {ptype}")
            out(f"{'=' * 80}")
            out("")

            result = await _handle_create(
                project_path=str(proj_path),
                main_source=main_src,
                framework_type=framework,
                app_type=app_type,
                platforms=["Win32", "Win64"],
                configs=["Debug", "Release"],
            )
            out(f"**Generator**: {result.content[0].text}")
            out("")

            parser = DprojParser(str(proj_path))
            parser.parse()
            info = parser.get_project_info()
            out(f"**Parsed Info**:")
            out(f"- FrameworkType: {info.get('framework_type', 'N/A')}")
            out(f"- AppType: {info.get('app_type', 'N/A')}")
            out(f"- MainSource: {parser.get_main_source() or 'N/A'}")
            out("")

            tree = ET.parse(str(proj_path))
            root = tree.getroot()
            pgs = root.findall(_tag("PropertyGroup"))
            pg_by_cond = {}
            for pg in pgs:
                cond = pg.get("Condition", "(unconditional)")
                pg_by_cond[cond] = pg

            out(f"Total PGs: {len(pgs)}")
            out("")

            ok_map = {}
            fail_map = {}

            # ═══════════════════════════════════════════
            # 1. HIERARCHY PGs (compound conditions)
            # ═══════════════════════════════════════════
            out("### 1. Platform PG Hierarchies (Win32 + Win64)")
            for base in ["Base", "Cfg_1", "Cfg_2"]:
                for plat in ["Win32", "Win64"]:
                    cond = (f"('$(Platform)'=='{plat}' and '$({base})'=='true')"
                            f" or '$({base}_{plat})'!=''")
                    ok = cond in pg_by_cond
                    label = f"{base}_{plat} hierarchy PG"
                    if ok:
                        ok_map[label] = True
                    else:
                        fail_map[label] = f"`{base}_{plat}` hierarchy PG MISSING"
                    out(f"  {'✅' if ok else '❌'} {label}")
            out("")

            # ═══════════════════════════════════════════
            # 2. SHARED PROPERTY PGs (simple conditions)
            # ═══════════════════════════════════════════
            out("### 2. Shared Property PG Existence (simple conditions)")
            shared_keys = [
                "'$(Base)'!=''",
                "'$(Base_Win32)'!=''",
                "'$(Base_Win64)'!=''",
                "'$(Cfg_1)'!=''",
                "'$(Cfg_2)'!=''",
                "'$(Cfg_1_Win32)'!=''",
                "'$(Cfg_1_Win64)'!=''",
                "'$(Cfg_2_Win32)'!=''",
                "'$(Cfg_2_Win64)'!=''",
            ]
            for sk in shared_keys:
                ok = sk in pg_by_cond
                label = f"Shared PG: `{sk}`"
                if ok:
                    ok_map[label] = True
                else:
                    fail_map[label] = f"PG `{sk}` NOT generated by template"
                out(f"  {'✅' if ok else '❌'} {label}")
            out("")

            # ═══════════════════════════════════════════
            # 3. BT_BuildType
            # ═══════════════════════════════════════════
            out("### 3. BT_BuildType Presence")
            bt_targets = [
                ("'$(Cfg_1)'!=''",       "Cfg_1"),
                ("'$(Cfg_2)'!=''",       "Cfg_2"),
                ("'$(Base_Win32)'!=''",  "Base_Win32"),
                ("'$(Base_Win64)'!=''",  "Base_Win64"),
                ("'$(Cfg_1_Win32)'!=''", "Cfg_1_Win32"),
                ("'$(Cfg_1_Win64)'!=''", "Cfg_1_Win64"),
                ("'$(Cfg_2_Win32)'!=''", "Cfg_2_Win32"),
                ("'$(Cfg_2_Win64)'!=''", "Cfg_2_Win64"),
            ]
            for cond, label in bt_targets:
                pg = pg_by_cond.get(cond)
                if pg is None:
                    fail_map[f"BT_BuildType in {label}"] = (
                        f"Shared PG `{cond}` not generated"
                    )
                    out(f"  ❌ BT_BuildType in {label}: shared PG `{cond}` doesn't exist")
                    continue
                elem = pg.find(_tag("BT_BuildType"))
                if elem is not None:
                    ok_map[f"BT_BuildType in {label}"] = f"value={elem.text}"
                    out(f"  ✅ BT_BuildType in {label}: {elem.text}")
                else:
                    fail_map[f"BT_BuildType in {label}"] = (
                        f"PG `{cond}` exists but BT_BuildType MISSING"
                    )
                    out(f"  ❌ BT_BuildType in {label}: PG exists but element missing")
            out("")

            # ═══════════════════════════════════════════
            # 4. DCC_ConsoleTarget (Console only)
            # ═══════════════════════════════════════════
            out("### 4. DCC_ConsoleTarget")
            if app_type == "Console":
                ct_targets = [
                    ("'$(Base)'!=''",       "'$(Base)'"),
                    ("'$(Base_Win32)'!=''", "Base_Win32"),
                    ("'$(Base_Win64)'!=''", "Base_Win64"),
                    ("'$(Cfg_1)'!=''",      "Cfg_1"),
                    ("'$(Cfg_2)'!=''",      "Cfg_2"),
                    ("'$(Cfg_1_Win32)'!=''","Cfg_1_Win32"),
                    ("'$(Cfg_1_Win64)'!=''","Cfg_1_Win64"),
                    ("'$(Cfg_2_Win32)'!=''","Cfg_2_Win32"),
                    ("'$(Cfg_2_Win64)'!=''","Cfg_2_Win64"),
                ]
                for cond, label in ct_targets:
                    pg = pg_by_cond.get(cond)
                    if pg is None:
                        fail_map[f"DCC_ConsoleTarget in {label}"] = (
                            f"Shared PG `{cond}` not generated"
                        )
                        out(f"  ❌ DCC_ConsoleTarget in {label}: shared PG `{cond}` doesn't exist")
                        continue
                    elem = pg.find(_tag("DCC_ConsoleTarget"))
                    if elem is not None:
                        ok_map[f"DCC_ConsoleTarget in {label}"] = elem.text
                        out(f"  ✅ DCC_ConsoleTarget in {label}: {elem.text}")
                    else:
                        fail_map[f"DCC_ConsoleTarget in {label}"] = (
                            f"PG `{cond}` exists but DCC_ConsoleTarget MISSING"
                        )
                        out(f"  ❌ DCC_ConsoleTarget in {label}: PG exists but element missing")
            else:
                out("  (N/A — not a Console project)")
            out("")

            # ═══════════════════════════════════════════
            # 5. PACKAGE-SPECIFIC
            # ═══════════════════════════════════════════
            out("### 5. Package-Specific Elements")
            if app_type == "Package":
                base_pg = pg_by_cond.get("'$(Base)'!=''")
                if base_pg is None:
                    out("  ❌ '$(Base)'!='' PG not found")
                else:
                    for elem_name in ["GenPackage", "GenDll", "RuntimeOnlyPackage"]:
                        elem = base_pg.find(_tag(elem_name))
                        if elem is not None:
                            ok_map[f"Package: `{elem_name}`"] = elem.text
                            out(f"  ✅ `{elem_name}` = {elem.text}")
                        else:
                            fail_map[f"Package: `{elem_name}`"] = f"MISSING"
                            out(f"  ❌ `{elem_name}`: MISSING")
            else:
                out("  (N/A — not a Package)")
            out("")

            # ═══════════════════════════════════════════
            # 6. ProjectExtensions
            # ═══════════════════════════════════════════
            out("### 6. ProjectExtensions Structure")
            pe = root.find(_tag("ProjectExtensions"))
            if pe is None:
                fail_map["ProjectExtensions"] = "NOT FOUND"
                out("  ❌ ProjectExtensions NOT found")
            else:
                bp = pe.find(_tag("Borland.Personality"))
                if bp is not None:
                    ok_map["Borland.Personality"] = bp.text
                    out(f"  ✅ Borland.Personality: {bp.text}")
                else:
                    fail_map["Borland.Personality"] = "MISSING"
                    out("  ❌ Borland.Personality: MISSING")

                bpt = pe.find(_tag("Borland.ProjectType"))
                if bpt is not None:
                    ok_map["Borland.ProjectType"] = bpt.text or "(empty)"
                    out(f"  ✅ Borland.ProjectType: {bpt.text or '(empty)'}")
                else:
                    fail_map["Borland.ProjectType"] = "MISSING or empty"
                    out("  ❌ Borland.ProjectType: MISSING or empty")

                bproj = pe.find(_tag("BorlandProject"))
                if bproj is not None:
                    ok_map["BorlandProject exists"] = True
                    dp = bproj.find(_tag("Delphi.Personality"))
                    if dp is not None:
                        src = dp.find(_tag("Source"))
                        if src is not None:
                            src_item = src.find(_tag("Source"))
                            if src_item is not None:
                                ok_map["Source in ProjectExtensions"] = src_item.text
                                out(f"  ✅ Source: {src_item.text}")
                            else:
                                fail_map["Source > Source item"] = "MISSING"
                                out("  ❌ Source > Source child: MISSING")
                        else:
                            fail_map["Source element in PE"] = "MISSING"
                            out("  ❌ Source in Delphi.Personality: MISSING")
                    else:
                        fail_map["Delphi.Personality in BorlandProject"] = "MISSING"
                        out("  ❌ Delphi.Personality in BorlandProject: MISSING")
                else:
                    fail_map["BorlandProject"] = "MISSING"
                    out("  ❌ BorlandProject: MISSING")

                pfv = pe.find(_tag("ProjectFileVersion"))
                if pfv is not None:
                    ok_map["ProjectFileVersion"] = pfv.text
                    out(f"  ✅ ProjectFileVersion: {pfv.text}")
                else:
                    fail_map["ProjectFileVersion"] = "MISSING"
                    out("  ❌ ProjectFileVersion: MISSING")
            out("")

            # ═══════════════════════════════════════════
            # 7. BuildConfigurations
            # ═══════════════════════════════════════════
            out("### 7. BuildConfiguration Items")
            igs = root.findall(_tag("ItemGroup"))
            bcs = []
            for ig in igs:
                bcs.extend(ig.findall(_tag("BuildConfiguration")))
            bc_names = [bc.get("Include") for bc in bcs]
            for name in ["Base", "Debug", "Release"]:
                ok = name in bc_names
                lbl = f"BuildConfiguration: {name}"
                if ok:
                    ok_map[lbl] = True
                else:
                    fail_map[lbl] = f"BuildConfiguration `{name}` MISSING"
                out(f"  {'✅' if ok else '❌'} {lbl}")
            out("")

            # ═══════════════════════════════════════════
            # PER-PROJECT SUMMARY
            # ═══════════════════════════════════════════
            n_ok = len(ok_map)
            n_fail = len(fail_map)
            master_ok[ptype] = ok_map
            master_fail[ptype] = fail_map

            out(f"### Summary for {ptype}")
            out("")

            old_issues_map = {
                "VCL App": [
                    ("Base_Win64 hierarchy PG was missing", "Base_Win64 hierarchy PG"),
                    ("Cfg_1_Win64 hierarchy PG was missing", "Cfg_1_Win64 hierarchy PG"),
                    ("Cfg_2_Win64 hierarchy PG was missing", "Cfg_2_Win64 hierarchy PG"),
                    ("BT_BuildType missing in Cfg_1 PG", "BT_BuildType in Cfg_1"),
                    ("BT_BuildType missing in Cfg_1_Win32 PG", "BT_BuildType in Cfg_1_Win32"),
                ],
                "FMX App": [
                    ("Base_Win64 hierarchy PG was missing", "Base_Win64 hierarchy PG"),
                    ("Cfg_1_Win64 hierarchy PG was missing", "Cfg_1_Win64 hierarchy PG"),
                    ("Cfg_2_Win64 hierarchy PG was missing", "Cfg_2_Win64 hierarchy PG"),
                    ("BT_BuildType missing in Cfg_1 PG", "BT_BuildType in Cfg_1"),
                    ("BT_BuildType missing in Cfg_1_Win32 PG", "BT_BuildType in Cfg_1_Win32"),
                ],
                "Console App": [
                    ("Base_Win64 hierarchy PG was missing", "Base_Win64 hierarchy PG"),
                    ("Cfg_1_Win64 hierarchy PG was missing", "Cfg_1_Win64 hierarchy PG"),
                    ("Cfg_2_Win64 hierarchy PG was missing", "Cfg_2_Win64 hierarchy PG"),
                    ("DCC_ConsoleTarget missing in Cfg_1", "DCC_ConsoleTarget in Cfg_1"),
                    ("DCC_ConsoleTarget missing in Cfg_2", "DCC_ConsoleTarget in Cfg_2"),
                    ("DCC_ConsoleTarget missing in '$(Base)'", "DCC_ConsoleTarget in '$(Base)'"),
                    ("BT_BuildType missing in Cfg_1_Win32", "BT_BuildType in Cfg_1_Win32"),
                ],
                "Package": [
                    ("GenPackage was missing", "Package: `GenPackage`"),
                    ("RuntimeOnlyPackage was missing", "Package: `RuntimeOnlyPackage`"),
                    ("GenDll was missing", "Package: `GenDll`"),
                    ("ProjectFileVersion was missing", "ProjectFileVersion"),
                ],
                "Library": [],
            }

            fixed_list = []
            remain_list = []
            for old_desc, check_key in old_issues_map.get(ptype, []):
                if check_key in ok_map:
                    fixed_list.append(old_desc)
                elif check_key in fail_map:
                    remain_list.append(f"{old_desc}: {fail_map[check_key]}")

            if fixed_list:
                out("**FIXED vs previous run:**")
                for f in fixed_list:
                    out(f"  ✅ `{f}`")
            if remain_list:
                out("**REMAINING from previous run:**")
                for r in remain_list:
                    out(f"  ⚠️ `{r}`")

            old_keys = set(k for _, k in old_issues_map.get(ptype, []))
            new_fails = {k: v for k, v in fail_map.items() if k not in old_keys}
            if new_fails:
                out("**NEW gaps (not in previous run's check list):**")
                for k, v in new_fails.items():
                    out(f"  🆕 `{k}` — {v}")

            out("")
            if n_fail == 0:
                out(f"✅ **All {n_ok} checks passed!**")
            else:
                out(f"❌ **{n_fail}/{n_ok + n_fail} checks failing.**")
            out("")

    # ═══════════════════════════════════════════
    # OVERALL SUMMARY
    # ═══════════════════════════════════════════
    out(f"{'=' * 80}")
    out("## Overall Summary")
    out(f"{'=' * 80}")
    out("")
    out("| Project Type | Pass | Fail | Verdict |")
    out("|-------------|------|------|---------|")
    for ptype, _, _ in projects:
        n_ok = len(master_ok.get(ptype, {}))
        n_fail = len(master_fail.get(ptype, {}))
        if n_fail == 0:
            verdict = "✅ PASS"
        elif n_fail <= 5:
            verdict = "⚠️ MINOR GAPS"
        else:
            verdict = "❌ GAPS"
        out(f"| {ptype} | {n_ok} | {n_fail} | {verdict} |")
    out("")

    # ═══════════════════════════════════════════
    # CROSS-CUTTING ANALYSIS
    # ═══════════════════════════════════════════
    out("## Cross-Cutting Analysis: What Was Fixed vs What Remains")
    out("")

    out("### ✅ FIXED since previous run")
    out("")
    out("1. **Win64 Platform Hierarchy PGs** (`Base_Win64`, `Cfg_1_Win64`, `Cfg_2_Win64`)")
    out("   - `_add_property_group_hierarchy` now iterates ALL platforms")
    out("   - Previously only Win32 PGs were created")
    out("")
    out("2. **BT_BuildType in Cfg_1 and Cfg_2 shared property PGs**")
    out("   - Added: `<BT_BuildType>Debug</BT_BuildType>` in `'$(Cfg_1)'!=''`")
    out("   - Added: `<BT_BuildType>Release</BT_BuildType>` in `'$(Cfg_2)'!=''`")
    out("")
    out("3. **BT_BuildType in Base_Win32 and Base_Win64 shared property PGs**")
    out("   - Added in both platform-base PGs (value `Debug`)")
    out("")
    out("4. **GenPackage and GenDll for Package projects**")
    out("   - `<GenPackage>true</GenPackage>`, `<GenDll>true</GenDll>` in `'$(Base)'!=''` PG")
    out("")
    out("5. **DCC_ConsoleTarget in Base_Win32 and Base_Win64 PGs** for Console")
    out("   - `<DCC_ConsoleTarget>true</DCC_ConsoleTarget>` in both platform-base PGs")
    out("")
    out("### ❌ REMAINING gaps")
    out("")
    out("1. **Missing 4 shared property PGs** — `'$(Cfg_1_Win32)'!=''`, `'$(Cfg_1_Win64)'!=''`,")
    out("   `'$(Cfg_2_Win32)'!=''`, `'$(Cfg_2_Win64)'!=''` — these PGs are not generated at all.")
    out("   Consequently BT_BuildType and DCC_ConsoleTarget cannot be placed in them.")
    out("   - The hierarchy PGs (compound conditions) DO exist, but the shared property")
    out("     PGs where BT_BuildType/DCC_ConsoleTarget should live are absent")
    out("")
    out("2. **DCC_ConsoleTarget** missing in Cfg_1, Cfg_2 shared property PGs for Console")
    out("   - Only in Base_Win32/Base_Win64; not propagated to config-level PGs")
    out("")
    out("3. **RuntimeOnlyPackage** not generated for Package")
    out("")
    out("4. **ProjectFileVersion** missing for Package (only in else branch)")
    out("")
    out("### 🏗️ Missing shared PG table")
    out("")
    out("| Shared Property PG | Generated? | Contents |")
    out("|-------------------|-----------|----------|")
    out("| `'$(Base)'!=''` | ✅ | SanitizedProjectName, DCC_*, GenPackage (pkg) |")
    out("| `'$(Base_Win32)'!=''` | ✅ | Namespace, BT_BuildType, Manifest |")
    out("| `'$(Base_Win64)'!=''` | ✅ | Namespace, BT_BuildType |")
    out("| `'$(Cfg_1)'!=''` | ✅ | BT_BuildType, DCC_Define, Debug flags |")
    out("| `'$(Cfg_2)'!=''` | ✅ | BT_BuildType, DCC_Define, Release flags |")
    out("| `'$(Cfg_1_Win32)'!=''` | ❌ | Should have BT_BuildType, DCC_Define |")
    out("| `'$(Cfg_1_Win64)'!=''` | ❌ | Should have BT_BuildType, DCC_Define |")
    out("| `'$(Cfg_2_Win32)'!=''` | ❌ | Should have BT_BuildType, DCC_Define |")
    out("| `'$(Cfg_2_Win64)'!=''` | ❌ | Should have BT_BuildType, DCC_Define |")
    out("")

    return "\n".join(report_lines)


if __name__ == "__main__":
    report = asyncio.run(run())
    out_path = Path(__file__).resolve().parent / "test_dproj_comparison_fixed.md"
    out_path.write_text(report, encoding="utf-8")
    print(f"Report written to: {out_path}")
