from pathlib import Path
from tempfile import TemporaryDirectory
import json

from django.core.management import call_command
from django.test import TestCase, override_settings

from .utils import create_mobile_app, create_site


class MobileAppCommandTests(TestCase):
    def setUp(self):
        self.site = create_site()
        create_mobile_app(
            self.site,
            enable_push_notifications=True,
            enable_camera_uploads=True,
            enable_file_picker=True,
            enable_share_sheet=True,
            enable_qr_scanner=True,
            enable_secure_storage=True,
            enable_deep_links=True,
        )

    def test_mobileapp_generate_creates_project_files(self):
        with TemporaryDirectory() as tempdir:
            with override_settings(BASE_DIR=Path(tempdir)):
                call_command("mobileapp_generate", site_id=self.site.pk)
                output_dir = Path(tempdir) / "mobile_apps" / f"site_{self.site.pk}"
                self.assertTrue((output_dir / "package.json").exists())
                self.assertTrue((output_dir / "capacitor.config.ts").exists())
                self.assertTrue((output_dir / "dist" / "index.html").exists())
                self.assertTrue((output_dir / "README.md").exists())
                self.assertTrue((output_dir / "ANDROID.md").exists())
                self.assertTrue((output_dir / "IOS.md").exists())
                self.assertTrue((output_dir / "dist" / "manifest.webmanifest").exists())
                self.assertTrue((output_dir / "dist" / "service-worker.js").exists())
                self.assertTrue((output_dir / "dist" / "offline.html").exists())
                self.assertTrue((output_dir / "assets" / "app-config.json").exists())
                self.assertTrue((output_dir / "assets" / "mobile-bridge.js").exists())
                self.assertTrue((output_dir / "assets" / "branding.json").exists())
                self.assertTrue((output_dir / "assets" / "app-shell.css").exists())
                self.assertTrue((output_dir / "assets" / "app-shell.js").exists())
                self.assertTrue(
                    (output_dir / "dist" / "assets" / "app-shell.css").exists()
                )
                self.assertTrue(
                    (output_dir / "dist" / "assets" / "app-shell.js").exists()
                )
                self.assertTrue((output_dir / "assets" / "adaptive-icon.png").exists())
                self.assertTrue(
                    (output_dir / "assets" / "adaptive-icon-foreground.png").exists()
                )
                self.assertTrue(
                    (output_dir / "assets" / "adaptive-icon-background.png").exists()
                )
                self.assertTrue((output_dir / "assets" / "splash.png").exists())
                payload = json.loads(
                    (output_dir / "assets" / "app-config.json").read_text(
                        encoding="utf-8"
                    )
                )
                self.assertTrue(
                    payload["capability_planning"]["capabilities"],
                )
                self.assertTrue(payload["capability_planning"]["registry"])
                self.assertIn(
                    "push_notifications",
                    payload["capability_planning"]["enabled_capability_keys"],
                )
                self.assertIn(
                    "camera",
                    payload["capability_planning"]["implemented_capability_keys"],
                )
                self.assertIn(
                    "qr_scanner",
                    payload["capability_planning"]["implemented_capability_keys"],
                )
                self.assertIn(
                    "secure_storage",
                    payload["capability_planning"]["implemented_capability_keys"],
                )
                self.assertIn(
                    "push_notifications",
                    payload["capability_planning"]["planned_capability_keys"],
                )
                self.assertIn("branding_assets", payload)
                self.assertEqual(payload["app_version"], "1.0.0")
                self.assertEqual(payload["build_number"], 1)
                self.assertIn("native_ux", payload)
                self.assertTrue(payload["native_ux"]["safe_area_support"])
                self.assertTrue(payload["native_ux"]["network_state_handling"])
                self.assertEqual(
                    payload["native_ux"]["status_bar_color"],
                    self.site.mobileapp.status_bar_color,
                )
                self.assertEqual(
                    payload["runtime_policies"]["external_links"]["cross_origin"],
                    "open_in_system_browser",
                )
                self.assertFalse(
                    payload["runtime_policies"]["downloads"]["native_manager"]
                )
                self.assertEqual(
                    payload["pwa"]["manifest_path"], "./manifest.webmanifest"
                )
                self.assertTrue(payload["pwa"]["service_worker_registration"])
                package_json = json.loads(
                    (output_dir / "package.json").read_text(encoding="utf-8")
                )
                self.assertIn("mobileApp", package_json)
                self.assertIn("devDependencies", package_json)
                self.assertEqual(package_json["mobileApp"]["version"], "1.0.0")
                self.assertEqual(package_json["mobileApp"]["buildNumber"], 1)
                self.assertTrue(package_json["mobileApp"]["capabilityRegistry"])
                self.assertIn(
                    "external_links",
                    package_json["mobileApp"]["implementedCapabilities"],
                )
                self.assertIn(
                    "push_notifications",
                    package_json["mobileApp"]["plannedCapabilities"],
                )
                self.assertEqual(
                    package_json["devDependencies"]["typescript"], "^5.6.3"
                )
                self.assertIn(
                    "@capacitor/push-notifications",
                    package_json["mobileApp"]["recommendedCapacitorPackages"],
                )
                self.assertIn(
                    "@capacitor/camera",
                    package_json["dependencies"],
                )
                self.assertIn(
                    "@capawesome/capacitor-file-picker",
                    package_json["dependencies"],
                )
                self.assertIn(
                    "@capacitor/share",
                    package_json["dependencies"],
                )
                self.assertIn(
                    "@capacitor-mlkit/barcode-scanning",
                    package_json["dependencies"],
                )
                self.assertIn(
                    "capacitor-secure-storage-plugin",
                    package_json["dependencies"],
                )
                self.assertEqual(
                    package_json["mobileApp"]["bridgeScript"],
                    "assets/mobile-bridge.js",
                )
                self.assertIn(
                    "@capacitor/share",
                    package_json["mobileApp"]["bridgeDependencies"],
                )
                self.assertIn(
                    "@capacitor-mlkit/barcode-scanning",
                    package_json["mobileApp"]["bridgeDependencies"],
                )
                self.assertEqual(
                    package_json["mobileApp"]["secureStorage"]["plugin"],
                    "capacitor-secure-storage-plugin",
                )
                self.assertEqual(
                    package_json["mobileApp"]["secureStorage"]["browserFallback"],
                    "memory_ephemeral",
                )
                self.assertIn(
                    "branding.json", package_json["mobileApp"]["generatedAssets"]
                )
                self.assertEqual(
                    package_json["mobileApp"]["appShell"]["css"],
                    "dist/assets/app-shell.css",
                )
                self.assertEqual(
                    package_json["mobileApp"]["appShell"]["js"],
                    "dist/assets/app-shell.js",
                )
                self.assertEqual(
                    package_json["mobileApp"]["appShell"]["statusBarColor"],
                    self.site.mobileapp.status_bar_color,
                )
                self.assertEqual(
                    package_json["mobileApp"]["pwa"]["manifest"],
                    "dist/manifest.webmanifest",
                )
                self.assertEqual(
                    package_json["mobileApp"]["pwa"]["serviceWorker"],
                    "dist/service-worker.js",
                )
                self.assertEqual(
                    package_json["mobileApp"]["runtimePolicies"]["externalLinks"][
                        "crossOriginBehavior"
                    ],
                    "open_in_system_browser",
                )
                generated_readme = (output_dir / "README.md").read_text(
                    encoding="utf-8"
                )
                self.assertIn("Android permission placeholders", generated_readme)
                self.assertIn("Deep link planning", generated_readme)
                self.assertIn("window.MandelMobileApp.pickImage()", generated_readme)
                self.assertIn(
                    "window.MandelMobileApp.shareCurrentPage()", generated_readme
                )
                self.assertIn("window.MandelMobileApp.scanQrCode()", generated_readme)
                self.assertIn(
                    "window.MandelMobileApp.secureStorage.set('session-token', 'demo')",
                    generated_readme,
                )
                self.assertIn("share_not_supported", generated_readme)
                self.assertIn("qr_scanner_not_supported", generated_readme)
                self.assertIn("memory-only", generated_readme)
                self.assertIn("Generated branding assets", generated_readme)
                self.assertIn("Implemented by the generator", generated_readme)
                self.assertIn("Requires local tooling", generated_readme)
                self.assertIn(
                    "What is not verified until local tooling is present",
                    generated_readme,
                )
                self.assertIn("Runtime diagnostics", generated_readme)
                self.assertIn(
                    "Cross-origin URLs should open in the system browser",
                    generated_readme,
                )
                android_note = (output_dir / "ANDROID.md").read_text(encoding="utf-8")
                self.assertIn("JDK 17 or newer", android_note)
                self.assertIn("./gradlew assembleDebug", android_note)
                ios_note = (output_dir / "IOS.md").read_text(encoding="utf-8")
                self.assertIn("full Xcode", ios_note)
                self.assertIn("CocoaPods", ios_note)
                self.assertIn("xcodebuild -workspace App.xcworkspace", ios_note)
                bridge_js = (output_dir / "assets" / "mobile-bridge.js").read_text(
                    encoding="utf-8"
                )
                self.assertIn("pickWithInput", bridge_js)
                self.assertIn("Capacitor?.Plugins?.Camera", bridge_js)
                self.assertIn("Capacitor?.Plugins?.FilePicker", bridge_js)
                self.assertIn("Capacitor?.Plugins?.Share", bridge_js)
                self.assertIn("Capacitor?.Plugins?.BarcodeScanner", bridge_js)
                self.assertIn("Capacitor?.Plugins?.SecureStoragePlugin", bridge_js)
                self.assertIn("navigator.share", bridge_js)
                self.assertIn("shareCurrentPage", bridge_js)
                self.assertIn("scanQrCode", bridge_js)
                self.assertIn("secureStorage", bridge_js)
                self.assertIn("secure_storage_not_supported", bridge_js)
                self.assertIn("browser_input_unavailable", bridge_js)
                self.assertIn("selection_cancelled", bridge_js)
                shell_html = (output_dir / "dist" / "index.html").read_text(
                    encoding="utf-8"
                )
                self.assertIn("viewport-fit=cover", shell_html)
                self.assertIn("./manifest.webmanifest", shell_html)
                self.assertIn("./assets/app-shell.css", shell_html)
                self.assertIn("./assets/app-shell.js", shell_html)
                self.assertIn("data-mobileapp-shell", shell_html)
                shell_css = (output_dir / "assets" / "app-shell.css").read_text(
                    encoding="utf-8"
                )
                self.assertIn("safe-area-inset-top", shell_css)
                self.assertIn(".mobileapp-shell__loading", shell_css)
                shell_js = (output_dir / "assets" / "app-shell.js").read_text(
                    encoding="utf-8"
                )
                self.assertIn('window.addEventListener("offline"', shell_js)
                self.assertIn("Ready to connect", shell_js)
                self.assertIn("window.MandelMobileAppDiagnostics", shell_js)
                self.assertIn("openExternal", shell_js)
                self.assertIn("describeDownload", shell_js)
                self.assertIn("shareSupported", shell_js)
                self.assertIn("qrScannerSupported", shell_js)
                self.assertIn("secureStorageSupported", shell_js)
                self.assertIn("secureStorageBackend", shell_js)
                self.assertIn("registerServiceWorker", shell_js)
                self.assertIn("manifestPath", shell_js)
                manifest_json = json.loads(
                    (output_dir / "dist" / "manifest.webmanifest").read_text(
                        encoding="utf-8"
                    )
                )
                self.assertEqual(manifest_json["name"], "Example App")
                self.assertEqual(manifest_json["display"], "standalone")
                self.assertEqual(manifest_json["theme_color"], "#112233")
                self.assertEqual(manifest_json["start_url"], "./index.html")
                self.assertEqual(manifest_json["scope"], "./")
                self.assertEqual(
                    manifest_json["categories"], ["business", "productivity"]
                )
                self.assertTrue(manifest_json["icons"])
                generated_service_worker = (
                    output_dir / "dist" / "service-worker.js"
                ).read_text(encoding="utf-8")
                self.assertIn("PRECACHE_URLS", generated_service_worker)
                self.assertIn("./manifest.webmanifest", generated_service_worker)
                self.assertIn("./offline.html", generated_service_worker)
                self.assertIn("cacheableDestinations", generated_service_worker)
                self.assertIn("/admin/", generated_service_worker)
                branding_json = json.loads(
                    (output_dir / "assets" / "branding.json").read_text(
                        encoding="utf-8"
                    )
                )
                self.assertEqual(branding_json["generator"], "ocyan.plugin.mobileapp")
                self.assertEqual(branding_json["appName"], "Example App")

    def test_mobileapp_verify_android_reports_local_requirements(self):
        from io import StringIO

        out = StringIO()
        call_command("mobileapp_verify_android", site_id=self.site.pk, stdout=out)
        output = out.getvalue()
        self.assertIn("package.json", output)
        self.assertIn("capacitor.config.ts", output)
        self.assertIn("branding.json", output)
        self.assertIn("node:", output)
        self.assertIn("ANDROID_HOME=", output)

    def test_mobileapp_generate_skips_native_upload_dependencies_when_disabled(self):
        disabled_site = create_site("disabled.example.com", "Disabled", False)
        create_mobile_app(
            disabled_site,
            enable_camera_uploads=False,
            enable_file_picker=False,
            enable_share_sheet=False,
        )
        with TemporaryDirectory() as tempdir:
            with override_settings(BASE_DIR=Path(tempdir)):
                call_command("mobileapp_generate", site_id=disabled_site.pk)
                output_dir = Path(tempdir) / "mobile_apps" / f"site_{disabled_site.pk}"
                package_json = json.loads(
                    (output_dir / "package.json").read_text(encoding="utf-8")
                )
                self.assertNotIn("@capacitor/camera", package_json["dependencies"])
                self.assertNotIn(
                    "@capawesome/capacitor-file-picker",
                    package_json["dependencies"],
                )
                self.assertFalse((output_dir / "assets" / "mobile-bridge.js").exists())

    def test_mobileapp_check_outputs_status(self):
        from io import StringIO

        out = StringIO()
        call_command("mobileapp_check", site_id=self.site.pk, stdout=out)
        output = out.getvalue()
        self.assertIn("[OK] https", output)
        self.assertIn("PWA support is enabled", output)
        self.assertIn("native_capabilities", output)
        self.assertIn("ios_camera_permission", output)
        self.assertIn("bridge_integration", output)
        self.assertIn("branding_manifest", output)

    def test_mobileapp_check_reports_missing_icon_and_splash(self):
        from io import StringIO

        site = create_site("missing-branding.example.com", "Missing Branding", False)
        create_mobile_app(site, icon=None, splash_image=None)
        out = StringIO()
        call_command("mobileapp_check", site_id=site.pk, stdout=out)
        output = out.getvalue()
        self.assertIn("branding_icon", output)
        self.assertIn("branding_splash", output)
