"""
Backend Generator
==================
Real implementation — generates a complete cloud backend and wires the
React Native app to it. No mock data. No fake credentials.

Supported backends:
  supabase  — SQL schema + RLS policies + Edge Functions + Supabase client setup
  firebase  — Firestore rules + Cloud Functions scaffold + Firebase SDK setup
  appwrite  — Appwrite collection config + SDK setup

Each backend generator:
  1. Uses Groq to generate backend-specific configuration/code
  2. Writes backend files to a /backend/ subdirectory
  3. Rewrites the React Native service layer to use real cloud calls
  4. Updates .env.example with backend env vars
  5. Generates a backend/README.md with exact deploy steps
"""
from __future__ import annotations

import json
from pathlib import Path
from typing import Optional

from forge.core.session import GenerationSession, AppPlan
from forge.core.api import GroqClient, strip_fences


# ── System prompts ────────────────────────────────────────────────────────────

SUPABASE_SCHEMA_SYSTEM = """You are a Principal Backend Engineer generating production Supabase SQL.

Rules:
- Use PostgreSQL best practices
- Every table has: id UUID PRIMARY KEY DEFAULT gen_random_uuid(), created_at TIMESTAMPTZ DEFAULT now(), updated_at TIMESTAMPTZ DEFAULT now()
- Add update trigger for updated_at on every table
- Enable Row Level Security on every table
- Write strict RLS policies: users can only see/edit their own data
- Add indexes on foreign keys and frequently queried fields
- Use ENUM types for status/category fields where appropriate
- Include Supabase Realtime REPLICA IDENTITY FULL on tables that need live updates

Output raw SQL only. No markdown. No explanation."""

SUPABASE_CLIENT_SYSTEM = """You are a Principal React Native / Supabase engineer.
Generate a production Supabase client and data service layer.

Rules:
- Use @supabase/supabase-js v2
- Read credentials from env: EXPO_PUBLIC_SUPABASE_URL and EXPO_PUBLIC_SUPABASE_ANON_KEY
- Implement full CRUD for every data model
- Implement real-time subscriptions where appropriate (RealtimeChannel)
- Implement auth: signIn, signUp, signOut, onAuthStateChange
- All operations properly typed with the generated schema types
- Use Supabase's typed client (pass Database generic type)
- Error handling: throw typed errors, never swallow exceptions

Output raw TypeScript only. No markdown."""

FIREBASE_RULES_SYSTEM = """You are a Senior Firebase Security Engineer.
Generate production Firestore security rules for a React Native app.

Rules:
- Default deny all
- Authenticated users can only read/write their own documents
- Validate data types and required fields in write rules
- Use match /databases/{database}/documents
- Include rules for every collection in the data model
- Add indexes configuration (firestore.indexes.json)

Output raw Firestore rules. No markdown."""

FIREBASE_SERVICE_SYSTEM = """You are a Principal React Native / Firebase engineer.
Generate a production Firebase service layer.

Rules:
- Use firebase/app, firebase/auth, firebase/firestore (modular SDK v9+)
- Read credentials from @env (react-native-dotenv) or expo-constants
- Implement full CRUD for every Firestore collection
- Use onSnapshot for real-time listeners (return unsubscribe function)
- Implement auth: signIn (email/password + Google), signUp, signOut, onAuthStateChange
- Proper TypeScript types throughout
- Batch writes for multi-document operations
- Offline persistence: initializeFirestore with localCache

Output raw TypeScript only. No markdown."""

APPWRITE_SERVICE_SYSTEM = """You are a Principal React Native / Appwrite engineer.
Generate a production Appwrite service layer.

Rules:
- Use appwrite SDK v13+
- Client initialised from env vars: EXPO_PUBLIC_APPWRITE_ENDPOINT and EXPO_PUBLIC_APPWRITE_PROJECT_ID
- Implement full CRUD using Databases service
- Implement auth using Account service
- Use Query helpers for filtering
- Implement file upload using Storage service where needed
- Full TypeScript types

Output raw TypeScript only. No markdown."""


# ── Supabase ──────────────────────────────────────────────────────────────────

async def _generate_supabase(
    session: GenerationSession,
    groq: GroqClient,
) -> dict[str, str]:
    """Returns dict of {relative_path: content}"""
    plan = session.plan
    files: dict[str, str] = {}

    models_json = json.dumps(plan.data_models, indent=2)
    screens_json = json.dumps([{"name": s["name"], "dataFlow": s.get("dataFlow", "")} for s in plan.screens], indent=2)

    session.log("BACKEND", "  Generating Supabase SQL schema + RLS policies…")

    schema_prompt = (
        f"App: {plan.app_name}\nDescription: {plan.description}\n\n"
        f"Data models:\n{models_json}\n\n"
        f"Screen data flows:\n{screens_json}\n\n"
        "Generate complete SQL: tables, RLS policies, indexes, triggers, enums."
    )
    schema_sql = await groq.complete(
        messages=[
            {"role": "system", "content": SUPABASE_SCHEMA_SYSTEM},
            {"role": "user", "content": schema_prompt},
        ],
        model=session.settings.plan_model,
        temperature=0.1,
        max_tokens=6000,
    )
    files["backend/supabase/migrations/001_initial.sql"] = strip_fences(schema_sql)
    session.log("BACKEND", "    ✓ SQL schema", "success")

    session.log("BACKEND", "  Generating Supabase TypeScript client + service layer…")
    client_prompt = (
        f"App: {plan.app_name}\n"
        f"Data models: {models_json}\n"
        f"Auth required: {plan.auth_required} ({plan.auth_method})\n\n"
        "Generate:\n"
        "1. src/services/supabase.ts — Supabase client initialisation + Database types\n"
        "2. src/services/api.ts — CRUD service for all models using the client\n"
        "Return ONLY src/services/supabase.ts content first, then a separator line '// === api.ts ===', then src/services/api.ts"
    )
    client_raw = await groq.complete(
        messages=[
            {"role": "system", "content": SUPABASE_CLIENT_SYSTEM},
            {"role": "user", "content": client_prompt},
        ],
        model=session.settings.plan_model,
        temperature=0.1,
        max_tokens=8000,
    )

    if "// === api.ts ===" in client_raw:
        parts = client_raw.split("// === api.ts ===", 1)
        files["src/services/supabase.ts"] = strip_fences(parts[0])
        files["src/services/api.ts"] = strip_fences(parts[1])
    else:
        files["src/services/supabase.ts"] = strip_fences(client_raw)

    session.log("BACKEND", "    ✓ Client + API service", "success")

    # Edge function scaffold for any external API integrations
    if plan.external_apis:
        session.log("BACKEND", "  Generating Edge Function scaffolds…")
        for api_name in plan.external_apis[:3]:
            fn_content = (
                f"// Supabase Edge Function: {api_name.lower()}-proxy\n"
                f"// Deploy: supabase functions deploy {api_name.lower()}-proxy\n\n"
                f'import {{ serve }} from "https://deno.land/std@0.168.0/http/server.ts"\n\n'
                f"serve(async (req) => {{\n"
                f"  const {{ method, body }} = req\n"
                f"  // TODO: implement {api_name} proxy logic\n"
                f"  // Access secrets: Deno.env.get('{api_name.upper()}_API_KEY')\n"
                f'  return new Response(JSON.stringify({{ ok: true }}), {{\n'
                f'    headers: {{ "Content-Type": "application/json" }},\n'
                f"  }})\n"
                f"}})\n"
            )
            fn_slug = api_name.lower().replace(" ", "-")
            files[f"backend/supabase/functions/{fn_slug}-proxy/index.ts"] = fn_content

    # config.toml
    files["backend/supabase/config.toml"] = (
        f'[api]\nenabled = true\nport = 54321\nschemas = ["public"]\n\n'
        f'[db]\nport = 54322\n\n'
        f'[studio]\nenabled = true\nport = 54323\n\n'
        f'[auth]\nenabled = true\nsite_url = "http://localhost:19006"\n'
        f'additional_redirect_urls = ["exp://localhost:19000"]\n\n'
        f'[storage]\nenabled = true\n'
    )

    # .env.example additions
    env_additions = (
        "\n# Supabase\n"
        "EXPO_PUBLIC_SUPABASE_URL=https://your-project.supabase.co\n"
        "EXPO_PUBLIC_SUPABASE_ANON_KEY=your-anon-key\n"
    )
    files["backend/.env.supabase.example"] = env_additions

    # Deploy README
    files["backend/supabase/README.md"] = f"""# {plan.app_name} — Supabase Backend

## Setup

```bash
# Install Supabase CLI
npm install -g supabase

# Login
supabase login

# Link to your project
supabase link --project-ref your-project-ref

# Run migrations
supabase db push

# Deploy Edge Functions
supabase functions deploy
```

## Environment Variables

Copy to your `.env` file:

```
EXPO_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
EXPO_PUBLIC_SUPABASE_ANON_KEY=your-anon-key
```

Find these at: https://supabase.com/dashboard/project/your-project/settings/api

## Local Development

```bash
supabase start          # starts local Postgres + Auth + Storage
supabase db reset       # resets and re-runs all migrations
supabase studio         # opens Supabase Studio at localhost:54323
```
"""

    return files


# ── Firebase ──────────────────────────────────────────────────────────────────

async def _generate_firebase(
    session: GenerationSession,
    groq: GroqClient,
) -> dict[str, str]:
    plan = session.plan
    files: dict[str, str] = {}
    models_json = json.dumps(plan.data_models, indent=2)

    session.log("BACKEND", "  Generating Firestore security rules…")
    rules_prompt = (
        f"App: {plan.app_name}\nData models:\n{models_json}\n\n"
        "Generate complete Firestore rules. One collection per data model. Strict auth."
    )
    rules = await groq.complete(
        messages=[
            {"role": "system", "content": FIREBASE_RULES_SYSTEM},
            {"role": "user", "content": rules_prompt},
        ],
        model=session.settings.plan_model,
        temperature=0.05,
        max_tokens=3000,
    )
    files["backend/firebase/firestore.rules"] = strip_fences(rules)
    session.log("BACKEND", "    ✓ Firestore rules", "success")

    session.log("BACKEND", "  Generating Firebase TypeScript service layer…")
    service_prompt = (
        f"App: {plan.app_name}\nData models:\n{models_json}\n"
        f"Auth: {plan.auth_method}\n\n"
        "Generate src/services/firebase.ts with full CRUD + auth."
    )
    service = await groq.complete(
        messages=[
            {"role": "system", "content": FIREBASE_SERVICE_SYSTEM},
            {"role": "user", "content": service_prompt},
        ],
        model=session.settings.plan_model,
        temperature=0.1,
        max_tokens=8000,
    )
    files["src/services/firebase.ts"] = strip_fences(service)
    session.log("BACKEND", "    ✓ Firebase service", "success")

    # Indexes
    collections = [m["name"].lower() + "s" for m in plan.data_models]
    indexes = {
        "indexes": [
            {
                "collectionGroup": col,
                "queryScope": "COLLECTION",
                "fields": [
                    {"fieldPath": "userId", "order": "ASCENDING"},
                    {"fieldPath": "createdAt", "order": "DESCENDING"},
                ],
            }
            for col in collections
        ]
    }
    files["backend/firebase/firestore.indexes.json"] = json.dumps(indexes, indent=2)

    # .firebaserc
    files["backend/firebase/.firebaserc"] = json.dumps({
        "projects": {"default": "your-firebase-project-id"},
    }, indent=2)

    # firebase.json
    files["backend/firebase/firebase.json"] = json.dumps({
        "firestore": {
            "rules": "firestore.rules",
            "indexes": "firestore.indexes.json",
        },
        "functions": {"source": "functions"},
        "hosting": {"public": "public"},
    }, indent=2)

    files["backend/.env.firebase.example"] = (
        "\n# Firebase\n"
        "EXPO_PUBLIC_FIREBASE_API_KEY=your-api-key\n"
        "EXPO_PUBLIC_FIREBASE_AUTH_DOMAIN=your-app.firebaseapp.com\n"
        "EXPO_PUBLIC_FIREBASE_PROJECT_ID=your-project-id\n"
        "EXPO_PUBLIC_FIREBASE_STORAGE_BUCKET=your-app.appspot.com\n"
        "EXPO_PUBLIC_FIREBASE_MESSAGING_SENDER_ID=123456789\n"
        "EXPO_PUBLIC_FIREBASE_APP_ID=1:123:web:abc\n"
    )

    files["backend/firebase/README.md"] = f"""# {plan.app_name} — Firebase Backend

## Setup

```bash
npm install -g firebase-tools
firebase login
firebase use your-project-id
firebase deploy --only firestore:rules,firestore:indexes
```

## Local Emulator

```bash
firebase emulators:start
```

Set `FIREBASE_EMULATOR=true` in `.env` to point the app at local emulators.
"""

    return files


# ── Appwrite ──────────────────────────────────────────────────────────────────

async def _generate_appwrite(
    session: GenerationSession,
    groq: GroqClient,
) -> dict[str, str]:
    plan = session.plan
    files: dict[str, str] = {}
    models_json = json.dumps(plan.data_models, indent=2)

    session.log("BACKEND", "  Generating Appwrite service layer…")
    service_prompt = (
        f"App: {plan.app_name}\nData models:\n{models_json}\n\n"
        "Generate src/services/appwrite.ts with full Databases CRUD + Account auth."
    )
    service = await groq.complete(
        messages=[
            {"role": "system", "content": APPWRITE_SERVICE_SYSTEM},
            {"role": "user", "content": service_prompt},
        ],
        model=session.settings.plan_model,
        temperature=0.1,
        max_tokens=6000,
    )
    files["src/services/appwrite.ts"] = strip_fences(service)
    session.log("BACKEND", "    ✓ Appwrite service", "success")

    # Collection config
    collections = []
    for model in plan.data_models:
        collection = {
            "name": model["name"],
            "databaseId": plan.slug,
            "collectionId": model["name"].lower(),
            "attributes": [
                {"key": f["name"], "type": f["type"], "required": f.get("required", False)}
                for f in model.get("fields", [])
            ],
        }
        collections.append(collection)

    files["backend/appwrite/collections.json"] = json.dumps({"collections": collections}, indent=2)

    files["backend/.env.appwrite.example"] = (
        "\n# Appwrite\n"
        "EXPO_PUBLIC_APPWRITE_ENDPOINT=https://cloud.appwrite.io/v1\n"
        "EXPO_PUBLIC_APPWRITE_PROJECT_ID=your-project-id\n"
    )

    return files


# ── Orchestrator ──────────────────────────────────────────────────────────────

BACKEND_DEPS: dict[str, list[str]] = {
    "supabase": ["@supabase/supabase-js@^2.43.4"],
    "firebase": [
        "firebase@^10.12.2",
        "@react-native-firebase/app@^20.1.0",
    ],
    "appwrite": ["appwrite@^14.0.1"],
}


async def run_backend_generator(
    session: GenerationSession,
    groq: GroqClient,
    backend: str = "supabase",
) -> dict[str, str]:
    """
    Generate a complete backend for the app.
    Returns dict of {relative_path: content} for all backend files.
    Writes files to project_dir and updates package.json with new deps.
    """
    plan = session.plan
    project_dir = session.project_dir

    agent_run = session.start_agent("BACKEND", f"Groq → {backend.title()}")
    session.log("BACKEND", f"Generating {backend.title()} backend for {plan.app_name}…")

    generator_map = {
        "supabase": _generate_supabase,
        "firebase": _generate_firebase,
        "appwrite": _generate_appwrite,
    }

    generator = generator_map.get(backend)
    if not generator:
        session.fail_agent(agent_run, f"Unknown backend: {backend}")
        return {}

    try:
        files = await generator(session, groq)
    except Exception as e:
        session.fail_agent(agent_run, str(e))
        session.log_error("BACKEND", f"Backend generation failed: {e}")
        return {}

    # Write to disk
    if project_dir:
        for rel_path, content in files.items():
            full = project_dir / rel_path
            full.parent.mkdir(parents=True, exist_ok=True)
            full.write_text(content, encoding="utf-8")
            session.add_file(rel_path, content, agent="backend", source="groq")

        # Update package.json
        pkg_path = project_dir / "package.json"
        if pkg_path.exists():
            try:
                pkg = json.loads(pkg_path.read_text())
                for dep in BACKEND_DEPS.get(backend, []):
                    if "@" in dep[1:]:
                        name, version = dep.rsplit("@", 1)
                    else:
                        name, version = dep, "latest"
                    pkg.setdefault("dependencies", {})[name] = version
                pkg_path.write_text(json.dumps(pkg, indent=2))
                session.log("BACKEND", f"  ✓ Added {backend} SDK to package.json")
            except Exception as e:
                session.log("BACKEND", f"  ⚠ Could not update package.json: {e}", "warning")

    session.log_success("BACKEND", f"✓ {backend.title()} backend generated — {len(files)} files")
    session.finish_agent(agent_run, backend=backend, files=len(files))

    return files
