"""
App verification — start apps, hit endpoints, health check with AI analysis.
"""

import asyncio
import os

from elgringo.cli.colors import Colors
from elgringo.cli.browse import normalize_url, fetch_url


async def run_verify(target: str, instruction: str = ""):
    """Verify a running app — hit endpoints, check health, report issues."""
    import subprocess as _sp
    import socket
    from elgringo.cli.run_task import run_single_task

    print(f"\n{Colors.BOLD}{'=' * 60}{Colors.RESET}")
    print(f"{Colors.CYAN}  EL GRINGO — VERIFY{Colors.RESET}")
    print(f"{Colors.BOLD}{'=' * 60}{Colors.RESET}\n")

    proc = None
    started_app = False
    base_url = ""

    try:
        # Determine if target is URL or directory
        if target.startswith(("http://", "https://")) or ":" in target and not os.path.isdir(target):
            base_url = normalize_url(target)
            print(f"  Target: {base_url} (URL)")
        elif os.path.isdir(target):
            # Try to detect and start the app
            start_cmd, port = detect_app_start_command(target)
            if not start_cmd:
                print(f"  {Colors.YELLOW}Could not detect how to start the app in {target}{Colors.RESET}")
                print("  Looked for: package.json, app.py, main.py, manage.py, Makefile")
                print(f"  {Colors.DIM}Tip: start the app yourself and run: elgringo verify localhost:PORT{Colors.RESET}\n")
                return

            base_url = f"http://localhost:{port}"
            print(f"  Directory: {target}")
            print(f"  Start command: {start_cmd}")
            print(f"  Expected at: {base_url}")
            print()

            # Check if already running
            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            already_running = sock.connect_ex(("localhost", port)) == 0
            sock.close()

            if already_running:
                print(f"  {Colors.GREEN}App already running on port {port}{Colors.RESET}")
            else:
                print(f"  {Colors.YELLOW}Starting app...{Colors.RESET}")
                proc = _sp.Popen(
                    start_cmd, shell=True, cwd=target,
                    stdout=_sp.DEVNULL, stderr=_sp.DEVNULL,
                )
                started_app = True

                # Wait for port to become available
                for i in range(20):  # 10 seconds max
                    await asyncio.sleep(0.5)
                    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                    if sock.connect_ex(("localhost", port)) == 0:
                        sock.close()
                        print(f"  {Colors.GREEN}App started on port {port}{Colors.RESET}")
                        break
                    sock.close()
                else:
                    print(f"  {Colors.RED}App failed to start within 10s{Colors.RESET}\n")
                    return
        else:
            # Treat as URL
            base_url = normalize_url(target)
            print(f"  Target: {base_url}")

        print()

        # Hit common endpoints
        endpoints = ["/", "/health", "/api", "/api/v1", "/status"]
        results = []

        print(f"  {Colors.CYAN}Checking endpoints:{Colors.RESET}")
        for endpoint in endpoints:
            url = base_url.rstrip("/") + endpoint
            page = await fetch_url(url)

            if page["status"] == 0 and page["error"]:
                status_str = f"{Colors.RED}UNREACHABLE{Colors.RESET}"
            elif page["status"] < 400:
                status_str = f"{Colors.GREEN}{page['status']}{Colors.RESET}"
            elif page["status"] == 404:
                status_str = f"{Colors.DIM}404{Colors.RESET}"
            else:
                status_str = f"{Colors.RED}{page['status']}{Colors.RESET}"

            print(f"    {endpoint:<16} {status_str} ({page['time']}s)")
            results.append(page)

        # Check for errors in responses
        error_indicators = ["traceback", "exception", "error", "500", "internal server error", "syntax error"]
        errors_found = []
        for r in results:
            content_lower = r["content"].lower()
            for indicator in error_indicators:
                if indicator in content_lower and r["status"] >= 400:
                    errors_found.append(f"{r['url']}: contains '{indicator}' (HTTP {r['status']})")
                    break

        print()
        if errors_found:
            print(f"  {Colors.RED}Issues detected:{Colors.RESET}")
            for err in errors_found:
                print(f"    {Colors.RED}!{Colors.RESET} {err}")
        else:
            working = [r for r in results if 200 <= r["status"] < 400]
            print(f"  {Colors.GREEN}{len(working)}/{len(results)} endpoints responding OK{Colors.RESET}")

        # Send to AI for analysis
        report_parts = []
        for r in results:
            if r["status"] > 0:
                preview = r["content"][:2000] if r["content"] else "(empty)"
                report_parts.append(
                    f"[{r['url']}] HTTP {r['status']} ({r['time']}s)\n{preview}"
                )

        if report_parts:
            print(f"\n  {Colors.CYAN}AI Analysis:{Colors.RESET}")
            prompt = (
                f"I verified a web application at {base_url}. "
                f"Here are the endpoint responses:\n\n"
                + "\n\n---\n\n".join(report_parts)
                + "\n\nAnalyze the health of this application. "
                + "Report: what's working, what's broken, any errors or concerns, "
                + "and recommendations."
            )
            if instruction:
                prompt += f"\n\nAdditional instruction: {instruction}"

            from elgringo.orchestrator import AIDevTeam
            team = AIDevTeam(enable_memory=False)
            if team.agents:
                await run_single_task(team, prompt, "quick")

    finally:
        # Clean up subprocess
        if proc and started_app:
            proc.terminate()
            try:
                proc.wait(timeout=5)
            except _sp.TimeoutExpired:
                proc.kill()
            print(f"\n  {Colors.DIM}Stopped app process.{Colors.RESET}")

    print()


def detect_app_start_command(dir_path: str) -> tuple:
    """Detect how to start an app in a directory. Returns (command, port)."""
    # Python apps
    for entry_file in ["app.py", "main.py", "server.py", "run.py"]:
        if os.path.isfile(os.path.join(dir_path, entry_file)):
            try:
                content = open(os.path.join(dir_path, entry_file)).read(5000)
                if "uvicorn" in content or "FastAPI" in content or "fastapi" in content:
                    return f"python3 {entry_file}", 8000
                if "Flask" in content or "flask" in content:
                    return f"python3 {entry_file}", 5000
            except Exception:
                pass
            return f"python3 {entry_file}", 8000

    if os.path.isfile(os.path.join(dir_path, "manage.py")):
        return "python3 manage.py runserver", 8000

    # Node.js apps
    pkg_json = os.path.join(dir_path, "package.json")
    if os.path.isfile(pkg_json):
        try:
            import json as _json
            pkg = _json.loads(open(pkg_json).read())
            scripts = pkg.get("scripts", {})
            if "dev" in scripts:
                return "npm run dev", 3000
            if "start" in scripts:
                return "npm start", 3000
        except Exception:
            pass
        return "npm start", 3000

    # Docker
    if os.path.isfile(os.path.join(dir_path, "docker-compose.yml")) or \
       os.path.isfile(os.path.join(dir_path, "docker-compose.yaml")):
        return "docker compose up", 8080

    # Makefile
    if os.path.isfile(os.path.join(dir_path, "Makefile")):
        try:
            content = open(os.path.join(dir_path, "Makefile")).read(5000)
            if "run:" in content or "serve:" in content or "start:" in content:
                for target in ["run", "serve", "start"]:
                    if f"{target}:" in content:
                        return f"make {target}", 8000
        except Exception:
            pass

    return "", 0
