"""Auto-detect the project type (iOS, Android, Web) for a given directory."""

from __future__ import annotations

from dataclasses import dataclass
from enum import Enum
from pathlib import Path
from typing import List


class Platform(str, Enum):
    IOS = "ios"
    ANDROID = "android"
    WEB = "web"


@dataclass
class DetectionResult:
    platform: Platform
    project_root: Path
    project_type: str  # e.g. "SwiftUI", "Gradle/Kotlin", "Next.js"
    confidence: float  # 0.0 – 1.0


# --- iOS detection -----------------------------------------------------------

_IOS_INDICATORS = {
    "*.xcodeproj": ("Xcode", 0.9),
    "*.xcworkspace": ("Xcode Workspace", 0.9),
    "Info.plist": ("iOS Plist", 0.7),
    "Package.swift": ("Swift Package", 0.6),
    "Podfile": ("CocoaPods", 0.7),
}


def _detect_ios(root: Path) -> List[DetectionResult]:
    results: List[DetectionResult] = []
    best_confidence = 0.0
    best_type = "iOS"

    for pattern, (label, conf) in _IOS_INDICATORS.items():
        if list(root.glob(pattern)):
            if conf > best_confidence:
                best_confidence = conf
                best_type = label

    # Check for Swift / ObjC source files as supporting evidence
    has_swift = bool(list(root.glob("**/*.swift"))[:1])
    has_objc = bool(list(root.glob("**/*.m"))[:1])

    if has_swift:
        best_type = "SwiftUI / UIKit"
        best_confidence = max(best_confidence, 0.8)
    elif has_objc:
        best_type = "Objective-C"
        best_confidence = max(best_confidence, 0.7)

    if best_confidence > 0:
        results.append(
            DetectionResult(Platform.IOS, root, best_type, best_confidence)
        )
    return results


# --- Android detection --------------------------------------------------------

_ANDROID_INDICATORS = {
    "build.gradle": ("Gradle", 0.8),
    "build.gradle.kts": ("Gradle (Kotlin DSL)", 0.8),
    "settings.gradle": ("Gradle", 0.6),
    "settings.gradle.kts": ("Gradle (Kotlin DSL)", 0.6),
    "AndroidManifest.xml": ("Android Manifest", 0.9),
    "**/AndroidManifest.xml": ("Android Manifest", 0.9),
}


def _detect_android(root: Path) -> List[DetectionResult]:
    results: List[DetectionResult] = []
    best_confidence = 0.0
    best_type = "Android"

    for pattern, (label, conf) in _ANDROID_INDICATORS.items():
        if list(root.glob(pattern)):
            if conf > best_confidence:
                best_confidence = conf
                best_type = label

    has_kotlin = bool(list(root.glob("**/*.kt"))[:1])
    has_java = bool(list(root.glob("**/*.java"))[:1])

    if has_kotlin:
        best_type = "Gradle / Kotlin"
        best_confidence = max(best_confidence, 0.8)
    elif has_java:
        best_type = "Gradle / Java"
        best_confidence = max(best_confidence, 0.7)

    if best_confidence > 0:
        results.append(
            DetectionResult(Platform.ANDROID, root, best_type, best_confidence)
        )
    return results


# --- Web detection ------------------------------------------------------------

_WEB_INDICATORS = {
    "package.json": ("Node.js", 0.5),
    # React / Next.js
    "next.config.js": ("Next.js", 0.9),
    "next.config.ts": ("Next.js", 0.9),
    "next.config.mjs": ("Next.js", 0.9),
    # Vue / Nuxt
    "nuxt.config.js": ("Nuxt", 0.9),
    "nuxt.config.ts": ("Nuxt", 0.9),
    "vue.config.js": ("Vue CLI", 0.8),
    # Svelte / SvelteKit
    "svelte.config.js": ("SvelteKit", 0.9),
    "svelte.config.ts": ("SvelteKit", 0.9),
    # Astro
    "astro.config.mjs": ("Astro", 0.9),
    "astro.config.ts": ("Astro", 0.9),
    # Vite
    "vite.config.js": ("Vite", 0.8),
    "vite.config.ts": ("Vite", 0.8),
    "vite.config.mjs": ("Vite", 0.8),
    # Angular
    "angular.json": ("Angular", 0.9),
    # Remix
    "remix.config.js": ("Remix", 0.9),
    "remix.config.ts": ("Remix", 0.9),
    # Gatsby
    "gatsby-config.js": ("Gatsby", 0.9),
    "gatsby-config.ts": ("Gatsby", 0.9),
    # Ember
    "ember-cli-build.js": ("Ember", 0.9),
    # Solid
    "solid-start.config.js": ("SolidStart", 0.9),
    "solid-start.config.ts": ("SolidStart", 0.9),
}


def _detect_web(root: Path) -> List[DetectionResult]:
    results: List[DetectionResult] = []
    best_confidence = 0.0
    best_type = "Web"

    for pattern, (label, conf) in _WEB_INDICATORS.items():
        if (root / pattern).exists():
            if conf > best_confidence:
                best_confidence = conf
                best_type = label

    if best_confidence > 0:
        results.append(
            DetectionResult(Platform.WEB, root, best_type, best_confidence)
        )
    return results


# --- Cross-platform detection ------------------------------------------------


def _detect_flutter(root: Path) -> List[DetectionResult]:
    """Detect Flutter projects via pubspec.yaml."""
    pubspec = root / "pubspec.yaml"
    if not pubspec.exists():
        return []
    try:
        content = pubspec.read_text(errors="ignore")
    except OSError:
        return []
    if "flutter" not in content:
        return []

    results: List[DetectionResult] = []
    if (root / "ios").is_dir():
        results.append(DetectionResult(Platform.IOS, root, "Flutter", 0.9))
    if (root / "android").is_dir():
        results.append(DetectionResult(Platform.ANDROID, root, "Flutter", 0.9))
    if (root / "web").is_dir():
        results.append(DetectionResult(Platform.WEB, root, "Flutter Web", 0.9))
    # If no platform dirs yet, still detect based on Dart source
    if not results and bool(list(root.glob("lib/**/*.dart"))[:1]):
        results.append(DetectionResult(Platform.IOS, root, "Flutter", 0.7))
        results.append(DetectionResult(Platform.ANDROID, root, "Flutter", 0.7))
    return results


def _detect_react_native(root: Path) -> List[DetectionResult]:
    """Detect React Native projects via metro config or package.json."""
    rn_configs = ["metro.config.js", "metro.config.ts", "react-native.config.js"]
    has_rn_config = any((root / f).exists() for f in rn_configs)

    if not has_rn_config:
        pkg_json = root / "package.json"
        if not pkg_json.exists():
            return []
        try:
            content = pkg_json.read_text(errors="ignore")
        except OSError:
            return []
        if '"react-native"' not in content:
            return []

    results: List[DetectionResult] = []
    if (root / "ios").is_dir():
        results.append(DetectionResult(Platform.IOS, root, "React Native", 0.9))
    if (root / "android").is_dir():
        results.append(DetectionResult(Platform.ANDROID, root, "React Native", 0.9))
    results.append(DetectionResult(Platform.WEB, root, "React Native", 0.8))
    return results


def _detect_maui(root: Path) -> List[DetectionResult]:
    """Detect .NET MAUI / Xamarin projects via .csproj files."""
    csproj_files = list(root.glob("**/*.csproj"))[:5]
    if not csproj_files:
        return []

    for csproj in csproj_files:
        try:
            content = csproj.read_text(errors="ignore")
        except OSError:
            continue
        if "UseMaui" in content or "Xamarin" in content:
            return [
                DetectionResult(Platform.IOS, root, ".NET MAUI", 0.9),
                DetectionResult(Platform.ANDROID, root, ".NET MAUI", 0.9),
            ]
    return []


# --- Public API ---------------------------------------------------------------


def detect_platforms(project_path: str | Path) -> List[DetectionResult]:
    """Return all detected platforms for the given project directory, sorted by confidence."""
    root = Path(project_path).resolve()
    if not root.is_dir():
        raise FileNotFoundError(f"Not a directory: {root}")

    detections: List[DetectionResult] = []
    detections.extend(_detect_ios(root))
    detections.extend(_detect_android(root))
    detections.extend(_detect_web(root))
    detections.extend(_detect_flutter(root))
    detections.extend(_detect_react_native(root))
    detections.extend(_detect_maui(root))

    # Deduplicate: keep highest confidence per platform
    seen: dict[Platform, DetectionResult] = {}
    for d in detections:
        if d.platform not in seen or d.confidence > seen[d.platform].confidence:
            seen[d.platform] = d
    detections = list(seen.values())

    detections.sort(key=lambda d: d.confidence, reverse=True)
    return detections
