#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Quant Engine UI Completeness Test
Playwright를 사용한 자동화 DOM 분석 및 완성도 평가
"""

import asyncio
import json
import sys
import os
from datetime import datetime
from pathlib import Path

if sys.platform == "win32":
    import io
    sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')

from playwright.async_api import async_playwright, Page

BASE_URL = "http://localhost:5265"

class UIAnalyzer:
    """MudBlazor UI 완성도 분석"""

    def __init__(self):
        self.results = {
            "timestamp": datetime.now().isoformat(),
            "base_url": BASE_URL,
            "tests": {},
            "score": 0,
            "issues": [],
            "recommendations": []
        }

    async def run_all_tests(self):
        """모든 테스트 실행"""
        async with async_playwright() as p:
            browser = await p.chromium.launch(headless=True)
            page = await browser.new_page()

            try:
                # 1. 페이지 로딩 테스트
                await self.test_page_load(page)

                # 2. MudBlazor 요소 검증
                await self.test_mudblazor_components(page)

                # 3. 레이아웃 검증
                await self.test_layout_structure(page)

                # 4. Dashboard 콘텐츠 검증
                await self.test_dashboard_content(page)

                # 5. 네비게이션 검증
                await self.test_navigation(page)

                # 6. 반응형 디자인 검증
                await self.test_responsive_design(page)

                # 7. 접근성 검증
                await self.test_accessibility(page)

                # 8. 성능 메트릭 수집
                await self.test_performance(page)

            finally:
                await browser.close()

        # 점수 계산 및 리포트 생성
        self.calculate_score()
        return self.results

    async def test_page_load(self, page: Page):
        """페이지 로드 테스트"""
        test_name = "page_load"
        self.results["tests"][test_name] = {"status": "PENDING", "checks": []}

        try:
            response = await page.goto(BASE_URL, wait_until="networkidle")

            status_ok = response.status == 200
            self.results["tests"][test_name]["checks"].append({
                "name": "HTTP Status 200",
                "passed": status_ok,
                "value": response.status
            })

            # 타이틀 확인
            title = await page.title()
            title_ok = "Dashboard" in title or "Quant Engine" in title
            self.results["tests"][test_name]["checks"].append({
                "name": "Page Title",
                "passed": title_ok,
                "value": title
            })

            # 로드 시간
            metrics = await page.evaluate("() => window.performance.timing")
            load_time = metrics.get("loadEventEnd", 0) - metrics.get("navigationStart", 0)
            load_ok = load_time < 5000  # 5초 이내
            self.results["tests"][test_name]["checks"].append({
                "name": "Load Time < 5s",
                "passed": load_ok,
                "value": f"{load_time}ms"
            })

            self.results["tests"][test_name]["status"] = "PASS" if all(
                c["passed"] for c in self.results["tests"][test_name]["checks"]
            ) else "FAIL"

        except Exception as e:
            self.results["tests"][test_name]["status"] = "ERROR"
            self.results["issues"].append(f"Page Load Error: {str(e)}")

    async def test_mudblazor_components(self, page: Page):
        """MudBlazor 컴포넌트 검증"""
        test_name = "mudblazor_components"
        self.results["tests"][test_name] = {"status": "PENDING", "components": []}

        components = {
            "MudLayout": "div.mud-layout",
            "MudAppBar": "header.mud-appbar",
            "MudDrawer": "aside.mud-drawer",
            "MudMainContent": "main.mud-main-content",
            "MudCard": "div.mud-card",
            "MudText": "p.mud-typography",
            "MudButton": "button",
            "MudIcon": "svg.mud-icon-root"
        }

        for component_name, selector in components.items():
            try:
                count = await page.locator(selector).count()
                found = count > 0
                self.results["tests"][test_name]["components"].append({
                    "name": component_name,
                    "selector": selector,
                    "found": found,
                    "count": count
                })
            except Exception as e:
                self.results["tests"][test_name]["components"].append({
                    "name": component_name,
                    "selector": selector,
                    "found": False,
                    "error": str(e)
                })

        found_count = sum(1 for c in self.results["tests"][test_name]["components"] if c["found"])
        self.results["tests"][test_name]["status"] = "PASS" if found_count >= 4 else "FAIL"

    async def test_layout_structure(self, page: Page):
        """레이아웃 구조 검증"""
        test_name = "layout_structure"
        self.results["tests"][test_name] = {"status": "PENDING", "checks": []}

        # 1. MudLayout 존재
        layout_exists = await page.locator("div.mud-layout").count() > 0
        self.results["tests"][test_name]["checks"].append({
            "name": "MudLayout exists",
            "passed": layout_exists
        })

        # 2. AppBar 존재
        appbar_exists = await page.locator("header.mud-appbar").count() > 0
        self.results["tests"][test_name]["checks"].append({
            "name": "MudAppBar exists",
            "passed": appbar_exists
        })

        # 3. Drawer 존재
        drawer_exists = await page.locator("aside.mud-drawer").count() > 0
        self.results["tests"][test_name]["checks"].append({
            "name": "MudDrawer exists",
            "passed": drawer_exists
        })

        # 4. MainContent 존재
        main_exists = await page.locator("main.mud-main-content").count() > 0
        self.results["tests"][test_name]["checks"].append({
            "name": "MudMainContent exists",
            "passed": main_exists
        })

        # 5. MudText 최소 3개 (헤더 등)
        text_count = await page.locator("p.mud-typography, h1, h2, h3, h4, h5, h6").count()
        text_ok = text_count >= 3
        self.results["tests"][test_name]["checks"].append({
            "name": f"Text elements >= 3 (found {text_count})",
            "passed": text_ok
        })

        self.results["tests"][test_name]["status"] = "PASS" if all(
            c["passed"] for c in self.results["tests"][test_name]["checks"]
        ) else "FAIL"

    async def test_dashboard_content(self, page: Page):
        """Dashboard 콘텐츠 검증"""
        test_name = "dashboard_content"
        self.results["tests"][test_name] = {"status": "PENDING", "elements": []}

        elements = {
            "Dashboard Title": "text=Dashboard",
            "Status Card": "text=Status",
            "Active Locks": "text=Active Locks",
            "System Info": "text=System Information",
            "Connected Badge": "text=Connected"
        }

        for elem_name, selector in elements.items():
            try:
                found = await page.locator(f"text={selector.replace('text=', '')}").count() > 0
                self.results["tests"][test_name]["elements"].append({
                    "name": elem_name,
                    "found": found
                })
            except:
                self.results["tests"][test_name]["elements"].append({
                    "name": elem_name,
                    "found": False
                })

        found_count = sum(1 for e in self.results["tests"][test_name]["elements"] if e["found"])
        self.results["tests"][test_name]["status"] = "PASS" if found_count >= 3 else "FAIL"

    async def test_navigation(self, page: Page):
        """네비게이션 검증"""
        test_name = "navigation"
        self.results["tests"][test_name] = {"status": "PENDING", "nav_items": []}

        nav_items = {
            "Dashboard": "text=Dashboard",
            "Portfolio": "text=Portfolio",
            "Analytics": "text=Analytics",
            "Reports": "text=Reports",
            "Settings": "text=Settings"
        }

        for item_name, selector in nav_items.items():
            try:
                found = await page.locator(selector).count() > 0
                self.results["tests"][test_name]["nav_items"].append({
                    "name": item_name,
                    "found": found
                })
            except:
                self.results["tests"][test_name]["nav_items"].append({
                    "name": item_name,
                    "found": False
                })

        found_count = sum(1 for n in self.results["tests"][test_name]["nav_items"] if n["found"])
        self.results["tests"][test_name]["status"] = "PASS" if found_count >= 3 else "FAIL"

    async def test_responsive_design(self, page: Page):
        """반응형 디자인 검증"""
        test_name = "responsive_design"
        self.results["tests"][test_name] = {"status": "PENDING", "viewports": []}

        viewports = [
            {"name": "Mobile (375x667)", "width": 375, "height": 667},
            {"name": "Tablet (768x1024)", "width": 768, "height": 1024},
            {"name": "Desktop (1920x1080)", "width": 1920, "height": 1080}
        ]

        for viewport in viewports:
            await page.set_viewport_size({"width": viewport["width"], "height": viewport["height"]})

            # 요소가 여전히 보이는지 확인
            visible = await page.locator("header.mud-appbar").is_visible()
            self.results["tests"][test_name]["viewports"].append({
                "name": viewport["name"],
                "size": f"{viewport['width']}x{viewport['height']}",
                "appbar_visible": visible
            })

        self.results["tests"][test_name]["status"] = "PASS" if all(
            v["appbar_visible"] for v in self.results["tests"][test_name]["viewports"]
        ) else "FAIL"

    async def test_accessibility(self, page: Page):
        """접근성 검증 (기본)"""
        test_name = "accessibility"
        self.results["tests"][test_name] = {"status": "PENDING", "checks": []}

        # 1. Lang 속성
        html_lang = await page.locator("html").get_attribute("lang")
        lang_ok = html_lang is not None
        self.results["tests"][test_name]["checks"].append({
            "name": "HTML lang attribute",
            "passed": lang_ok,
            "value": html_lang
        })

        # 2. Meta charset
        charset = await page.locator("meta[charset]").count() > 0
        self.results["tests"][test_name]["checks"].append({
            "name": "Meta charset",
            "passed": charset
        })

        # 3. Viewport meta
        viewport = await page.locator("meta[name='viewport']").count() > 0
        self.results["tests"][test_name]["checks"].append({
            "name": "Meta viewport",
            "passed": viewport
        })

        # 4. Heading hierarchy
        headings = await page.locator("h1, h2, h3, h4, h5, h6").count()
        heading_ok = headings > 0
        self.results["tests"][test_name]["checks"].append({
            "name": f"Heading hierarchy (found {headings})",
            "passed": heading_ok
        })

        self.results["tests"][test_name]["status"] = "PASS" if all(
            c["passed"] for c in self.results["tests"][test_name]["checks"]
        ) else "FAIL"

    async def test_performance(self, page: Page):
        """성능 메트릭 수집"""
        test_name = "performance"
        self.results["tests"][test_name] = {"status": "PASS", "metrics": {}}

        try:
            metrics = await page.evaluate("""
            () => ({
                domContentLoaded: performance.timing.domContentLoadedEventEnd - performance.timing.navigationStart,
                loadComplete: performance.timing.loadEventEnd - performance.timing.navigationStart,
                resources: performance.getEntriesByType('resource').length,
                memoryUsage: performance.memory ? Math.round(performance.memory.usedJSHeapSize / 1048576) : null
            })
            """)

            self.results["tests"][test_name]["metrics"] = {
                "DOM Content Loaded (ms)": metrics.get("domContentLoaded", 0),
                "Page Load Complete (ms)": metrics.get("loadComplete", 0),
                "Resources Loaded": metrics.get("resources", 0),
                "Memory Usage (MB)": metrics.get("memoryUsage")
            }
        except Exception as e:
            self.results["tests"][test_name]["status"] = "WARN"
            self.results["tests"][test_name]["error"] = str(e)

    def calculate_score(self):
        """완성도 점수 계산"""
        total_weight = 0
        total_score = 0

        weights = {
            "page_load": 15,
            "mudblazor_components": 20,
            "layout_structure": 20,
            "dashboard_content": 15,
            "navigation": 15,
            "responsive_design": 10,
            "accessibility": 5,
            "performance": 0  # 점수에 포함 안 함, 참고용
        }

        for test_name, weight in weights.items():
            if test_name in self.results["tests"]:
                test = self.results["tests"][test_name]
                if test["status"] == "PASS":
                    total_score += weight
                elif test["status"] == "FAIL":
                    # 부분 점수
                    if "checks" in test:
                        passed = sum(1 for c in test["checks"] if c.get("passed", False))
                        total = len(test["checks"])
                        total_score += weight * (passed / total)
                    elif "components" in test:
                        found = sum(1 for c in test["components"] if c.get("found", False))
                        total = len(test["components"])
                        total_score += weight * (found / total)

                total_weight += weight

        self.results["score"] = round(total_score, 1) if total_weight > 0 else 0
        self.results["max_score"] = total_weight

        # 권장사항 생성
        self.generate_recommendations()

    def generate_recommendations(self):
        """개선 권장사항 생성"""
        recommendations = []

        # Dashboard 콘텐츠 부족
        if self.results["tests"]["dashboard_content"]["status"] == "FAIL":
            recommendations.append({
                "category": "Content",
                "priority": "HIGH",
                "issue": "Dashboard 콘텐츠가 부족함",
                "suggestion": "스타투스 카드, 통계, 실시간 데이터 추가",
                "files": ["src/dotnet/QuantEngine.Web/Components/Pages/Dashboard.razor"]
            })

        # 네비게이션 미완성
        if self.results["tests"]["navigation"]["status"] == "FAIL":
            found = sum(1 for n in self.results["tests"]["navigation"]["nav_items"] if n["found"])
            recommendations.append({
                "category": "Navigation",
                "priority": "MEDIUM",
                "issue": f"네비게이션 항목 {found}/5개만 구현됨",
                "suggestion": "모든 네비게이션 항목 추가 (Analytics, Reports 등)",
                "files": ["src/dotnet/QuantEngine.Web/Components/Layout/NavMenu.razor"]
            })

        # 반응형 디자인 개선
        if self.results["tests"]["responsive_design"]["status"] == "FAIL":
            recommendations.append({
                "category": "UI/UX",
                "priority": "MEDIUM",
                "issue": "일부 뷰포트에서 레이아웃 깨짐",
                "suggestion": "MudContainer MaxWidth 조정, Grid 반응형 설정 확인",
                "files": ["src/dotnet/QuantEngine.Web/Components/Pages/Dashboard.razor"]
            })

        # 접근성 개선
        if self.results["tests"]["accessibility"]["status"] == "FAIL":
            recommendations.append({
                "category": "Accessibility",
                "priority": "LOW",
                "issue": "접근성 표준 미충족",
                "suggestion": "ARIA 라벨 추가, 색상 대비 개선",
                "files": ["src/dotnet/QuantEngine.Web/Components/App.razor"]
            })

        self.results["recommendations"] = recommendations

    async def take_screenshot(self, page: Page, filename: str):
        """스크린샷 캡처"""
        await page.screenshot(path=filename)
        print(f"✓ Screenshot: {filename}")


async def main():
    """메인 실행"""
    analyzer = UIAnalyzer()

    print("🧪 Quant Engine UI Completeness Test")
    print("=" * 70)
    print(f"URL: {BASE_URL}")
    print("=" * 70)

    try:
        results = await analyzer.run_all_tests()

        # 결과 출력
        print("\n📊 Test Results")
        print("-" * 70)

        for test_name, test_data in results["tests"].items():
            status = test_data["status"]
            status_emoji = "✅" if status == "PASS" else "❌" if status == "FAIL" else "⚠️"
            print(f"{status_emoji} {test_name.upper()}: {status}")

        print("\n" + "=" * 70)
        print(f"📈 Completeness Score: {results['score']}/{results['max_score']} ({results['score']/results['max_score']*100:.1f}%)")
        print("=" * 70)

        # 권장사항
        if results["recommendations"]:
            print("\n💡 Recommendations for Improvement")
            print("-" * 70)
            for i, rec in enumerate(results["recommendations"], 1):
                print(f"\n{i}. [{rec['priority']}] {rec['issue']}")
                print(f"   Category: {rec['category']}")
                print(f"   Suggestion: {rec['suggestion']}")
                print(f"   Files: {', '.join(rec['files'])}")

        # 결과 저장
        output_file = Path("Temp/ui_test_results.json")
        output_file.parent.mkdir(parents=True, exist_ok=True)
        output_file.write_text(json.dumps(results, indent=2, ensure_ascii=False))
        print(f"\n✓ Results saved: {output_file}")

        return results

    except Exception as e:
        print(f"\n❌ Error: {e}")
        import traceback
        traceback.print_exc()


if __name__ == "__main__":
    asyncio.run(main())
