import json
from pathlib import Path

from fastapi import HTTPException

from mosaygent.command_runner import run_command
from mosaygent.logger import get_logger

logger = get_logger(__name__)


class FirebaseConfigService:
    """Manages Firebase configuration file operations."""

    async def create_android_app(
        self,
        project_id: str,
        package_name: str,
        app_path: str,
        display_name: str | None = None,
    ) -> dict:
        """Create a new Android app in Firebase project or use existing one, and download config."""
        logger.info(f"Creating Android app with package name {package_name} in project {project_id}")

        app_display_name = display_name or package_name.split(".")[-1]
        app_id = None
        created_new = False

        result = await run_command(
            [
                "firebase", "apps:create", "android", app_display_name,
                "--package-name", package_name,
                "--project", project_id,
                "--json"
            ],
            timeout=60,
            raise_on_error=False
        )

        if result.success:
            try:
                response_data = json.loads(result.stdout)
                app_id = response_data.get("result", {}).get("appId")
                if app_id:
                    logger.info(f"Successfully created Android app with ID: {app_id}")
                    created_new = True
            except json.JSONDecodeError:
                logger.warning("Failed to parse create response, will try to find existing app")

        if not app_id:
            logger.info(f"App creation failed or app exists, looking up existing app with package {package_name}")
            try:
                app_id = await self._get_android_app_id(project_id, package_name)
                logger.info(f"Found existing Android app with ID: {app_id}")
            except HTTPException as e:
                logger.error(f"Failed to find existing Android app: {e.detail}")
                raise HTTPException(
                    status_code=500,
                    detail=f"Failed to create Android app and no existing app found with package {package_name}"
                )

        logger.info(f"Downloading google-services.json for app ID: {app_id}")
        config_result = await run_command(
            ["firebase", "apps:sdkconfig", "android", app_id, "--project", project_id],
            timeout=30
        )

        if not config_result.stdout.strip():
            logger.error("Firebase returned empty Android config")
            raise HTTPException(status_code=500, detail="Firebase returned empty Android config")

        try:
            config_json = json.loads(config_result.stdout)
        except json.JSONDecodeError as e:
            logger.error(f"Failed to parse google-services.json: {e}")
            raise HTTPException(status_code=500, detail="Invalid Android config JSON received from Firebase")

        target_dir = Path(app_path)
        target_file = target_dir / "google-services.json"

        logger.info(f"Ensuring target directory exists: {target_dir}")
        target_dir.mkdir(parents=True, exist_ok=True)

        logger.info(f"Writing google-services.json to: {target_file}")
        target_file.write_text(json.dumps(config_json, indent=2))

        logger.info(f"Successfully saved google-services.json to {target_file}")

        return {
            "app_id": app_id,
            "package_name": package_name,
            "display_name": app_display_name,
            "project_id": project_id,
            "config_file_path": str(target_file),
            "created_new": created_new,
        }

    async def _get_android_app_id(self, project_id: str, bundle_id: str) -> str:
        """Get Firebase Android app ID from bundle ID."""
        logger.info(f"Looking up Android app with bundle ID {bundle_id} in project {project_id}")

        result = await run_command(
            ["firebase", "apps:list", "--project", project_id, "--json"],
            timeout=30
        )

        try:
            apps_data = json.loads(result.stdout)
        except json.JSONDecodeError as e:
            logger.error(f"Failed to parse Firebase apps list: {e}")
            raise HTTPException(status_code=500, detail="Failed to parse Firebase apps list")

        if not apps_data or "result" not in apps_data:
            logger.error("No apps found in Firebase project")
            raise HTTPException(status_code=404, detail="No apps found in Firebase project")

        android_apps = [
            app for app in apps_data["result"]
            if app.get("platform") == "ANDROID" and app.get("appId")
        ]

        if not android_apps:
            logger.error("No Android apps found in Firebase project")
            raise HTTPException(status_code=404, detail="No Android apps found in project")

        matching_app = next(
            (app for app in android_apps if app.get("namespace") == bundle_id),
            None
        )

        if not matching_app:
            logger.error(f"No Android app found with bundle ID {bundle_id}")
            available_bundles = [app.get("namespace") for app in android_apps]
            raise HTTPException(
                status_code=404,
                detail=f"No Android app found with bundle ID {bundle_id}. Available: {available_bundles}"
            )

        app_id = matching_app["appId"]
        logger.info(f"Found Android app ID: {app_id}")
        return app_id

    async def _get_ios_app_id(self, project_id: str, bundle_id: str) -> str:
        """Get Firebase iOS app ID from bundle ID."""
        logger.info(f"Looking up iOS app with bundle ID {bundle_id} in project {project_id}")

        result = await run_command(
            ["firebase", "apps:list", "--project", project_id, "--json"],
            timeout=30
        )

        try:
            apps_data = json.loads(result.stdout)
        except json.JSONDecodeError as e:
            logger.error(f"Failed to parse Firebase apps list: {e}")
            raise HTTPException(status_code=500, detail="Failed to parse Firebase apps list")

        if not apps_data or "result" not in apps_data:
            logger.error("No apps found in Firebase project")
            raise HTTPException(status_code=404, detail="No apps found in Firebase project")

        ios_apps = [
            app for app in apps_data["result"]
            if app.get("platform") == "IOS" and app.get("appId")
        ]

        if not ios_apps:
            logger.error("No iOS apps found in Firebase project")
            raise HTTPException(status_code=404, detail="No iOS apps found in project")

        matching_app = next(
            (app for app in ios_apps if app.get("namespace") == bundle_id),
            None
        )

        if not matching_app:
            logger.error(f"No iOS app found with bundle ID {bundle_id}")
            available_bundles = [app.get("namespace") for app in ios_apps]
            raise HTTPException(
                status_code=404,
                detail=f"No iOS app found with bundle ID {bundle_id}. Available: {available_bundles}"
            )

        app_id = matching_app["appId"]
        logger.info(f"Found iOS app ID: {app_id}")
        return app_id

    async def fetch_configs(
        self,
        project_id: str,
        app_path: str,
        bundle_id: str,
        firebase_config_path: str | None = None
    ) -> dict:
        """Fetch Firebase config files (Android and iOS) and save to React Native app."""
        logger.info(
            f"Fetching Firebase configs for {bundle_id} "
            f"from project {project_id}"
        )

        android_app_id = await self._get_android_app_id(project_id, bundle_id)
        ios_app_id = await self._get_ios_app_id(project_id, bundle_id)

        logger.info(f"Downloading Android config for app ID: {android_app_id}")
        android_config_result = await run_command(
            ["firebase", "apps:sdkconfig", "android", android_app_id, "--project", project_id],
            timeout=30
        )

        if not android_config_result.stdout.strip():
            logger.error("Firebase returned empty Android config")
            raise HTTPException(status_code=500, detail="Firebase returned empty Android config")

        try:
            android_config_json = json.loads(android_config_result.stdout)
        except json.JSONDecodeError as e:
            logger.error(f"Failed to parse google-services.json: {e}")
            raise HTTPException(status_code=500, detail="Invalid Android config JSON received from Firebase")

        android_target_dir = Path(app_path) / "android" / "app"
        android_target_file = android_target_dir / "google-services.json"

        logger.info(f"Ensuring Android target directory exists: {android_target_dir}")
        android_target_dir.mkdir(parents=True, exist_ok=True)

        logger.info(f"Writing google-services.json to: {android_target_file}")
        android_target_file.write_text(json.dumps(android_config_json, indent=2))

        logger.info(f"Successfully saved google-services.json to {android_target_file}")

        logger.info(f"Downloading iOS config for app ID: {ios_app_id}")
        ios_config_result = await run_command(
            ["firebase", "apps:sdkconfig", "ios", ios_app_id, "--project", project_id],
            timeout=30
        )

        if not ios_config_result.stdout.strip():
            logger.error("Firebase returned empty iOS config")
            raise HTTPException(status_code=500, detail="Firebase returned empty iOS config")

        ios_target_dir = Path(app_path) / "ios"
        ios_target_file = ios_target_dir / "GoogleService-Info.plist"

        logger.info(f"Ensuring iOS target directory exists: {ios_target_dir}")
        ios_target_dir.mkdir(parents=True, exist_ok=True)

        logger.info(f"Writing GoogleService-Info.plist to: {ios_target_file}")
        ios_target_file.write_text(ios_config_result.stdout)

        logger.info(f"Successfully saved GoogleService-Info.plist to {ios_target_file}")

        return {
            "message": "Firebase configs downloaded successfully",
            "android_file_path": str(android_target_file),
            "ios_file_path": str(ios_target_file),
            "project_id": project_id,
            "bundle_id": bundle_id,
        }
