"""
IDG Admin Extension v2 — Phase 2 modules.

Adds:
- IDGMenu / IDGMenuItem — Header/Footer/Sidebar custom menus, multi-level, drag-drop reordering
- IDGSidebarWidget — drag-drop widget configuration per position (Top, Middle, Bottom)
- IDGBlock — HTML/Widget content blocks with conditional display rules
- IDGFrame & IDGFramePurchase — VIP frame marketplace (points-based)

Routes mounted under /api/idg/* alongside Phase 1.
"""
from fastapi import APIRouter, HTTPException, Depends
from fastapi.responses import Response as FastResponse
from pydantic import BaseModel, Field, ConfigDict
from typing import List, Optional, Dict, Any
from datetime import datetime, timezone
import uuid
import re


# ---------------- MODELS ----------------

class IDGMenuItem(BaseModel):
    model_config = ConfigDict(extra="ignore")
    id: Optional[str] = None
    label: str
    url: str = ""
    icon: str = "circle"
    target: str = "_self"  # _self, _blank
    order: int = 0
    visible_to: List[str] = Field(default_factory=lambda: ["all"])  # all, guest, user, vip, admin
    children: List["IDGMenuItem"] = Field(default_factory=list)


IDGMenuItem.model_rebuild()


class IDGMenu(BaseModel):
    model_config = ConfigDict(extra="ignore")
    id: Optional[str] = None
    location: str  # header, footer_quick, footer_community, footer_info, sidebar
    name: str
    items: List[IDGMenuItem] = Field(default_factory=list)


class IDGSidebarWidget(BaseModel):
    model_config = ConfigDict(extra="ignore")
    id: Optional[str] = None
    type: str  # recent_posts, categories, tags, stats, profile, calendar, html, online_users, top_users
    position: str  # top, middle, bottom
    title: str = ""
    settings: Dict[str, Any] = Field(default_factory=dict)
    # Common settings: { items_count: 5, show_date: true, show_author: true, show_avatar: true,
    #                    visibility: 'all'|'guest'|'user'|'vip'|'admin', custom_html: '' }
    enabled: bool = True
    order: int = 0


class IDGBlock(BaseModel):
    model_config = ConfigDict(extra="ignore")
    id: Optional[str] = None
    name: str
    type: str  # html, widget
    widget_type: Optional[str] = None  # if type=widget, reference to IDGSidebarWidget.type
    content: str = ""  # HTML content (for type=html)
    position: str  # header, content_top, content_bottom, sidebar, footer
    enabled: bool = True
    order: int = 0
    # Conditions
    show_on_pages: List[str] = Field(default_factory=lambda: ["all"])  # all, home, forums, topic, profile, custom slug
    show_on_devices: List[str] = Field(default_factory=lambda: ["all"])  # all, desktop, tablet, mobile
    show_to_users: List[str] = Field(default_factory=lambda: ["all"])  # all, guest, user, vip, admin


class IDGFrame(BaseModel):
    model_config = ConfigDict(extra="ignore")
    id: Optional[str] = None
    slug: str
    name: str
    description: str = ""
    image_url: str
    rarity: str = "common"  # common, rare, epic, legendary, mythic
    price_points: int = 100
    required_role: str = "user"  # user, vip
    enabled: bool = True
    stock: int = -1  # -1 = unlimited
    sales_count: int = 0
    order: int = 0


class IDGFramePurchaseRequest(BaseModel):
    frame_id: str


# ---------------- DEFAULT SEEDS ----------------

DEFAULT_HEADER_MENU = {
    "location": "header",
    "name": "Main Navigation",
    "items": [
        {"label": "ACASĂ", "url": "/", "icon": "home", "order": 0, "visible_to": ["all"], "children": []},
        {"label": "FORUM", "url": "/forums", "icon": "message-square", "order": 1, "visible_to": ["all"], "children": []},
        {"label": "TURNEE", "url": "/turnee", "icon": "trophy", "order": 2, "visible_to": ["all"], "children": []},
        {"label": "MEMBRI", "url": "/members", "icon": "users", "order": 3, "visible_to": ["all"], "children": []},
        {"label": "STATISTICI", "url": "/leaderboard", "icon": "bar-chart-2", "order": 4, "visible_to": ["all"], "children": []},
        {"label": "VIP", "url": "/vip", "icon": "crown", "order": 5, "visible_to": ["all"], "children": []},
        {"label": "DONEAZĂ", "url": "/donate", "icon": "heart", "order": 6, "visible_to": ["all"], "children": []},
    ],
}

DEFAULT_FOOTER_MENUS = [
    {
        "location": "footer_quick",
        "name": "Quick Links",
        "items": [
            {"label": "Forums", "url": "/forums", "icon": "message-square", "order": 0, "visible_to": ["all"], "children": []},
            {"label": "Members", "url": "/members", "icon": "users", "order": 1, "visible_to": ["all"], "children": []},
            {"label": "Leaderboard", "url": "/leaderboard", "icon": "trophy", "order": 2, "visible_to": ["all"], "children": []},
            {"label": "VIP Loadout", "url": "/loadout", "icon": "crown", "order": 3, "visible_to": ["all"], "children": []},
            {"label": "Donate", "url": "/donate", "icon": "heart", "order": 4, "visible_to": ["all"], "children": []},
        ],
    },
    {
        "location": "footer_info",
        "name": "Info",
        "items": [
            {"label": "Forum Rules", "url": "/p/rules", "icon": "book", "order": 0, "visible_to": ["all"], "children": []},
            {"label": "Terms of Service", "url": "/p/tos", "icon": "file-text", "order": 1, "visible_to": ["all"], "children": []},
            {"label": "Privacy Policy", "url": "/p/privacy", "icon": "shield", "order": 2, "visible_to": ["all"], "children": []},
            {"label": "Contact Admin", "url": "/messages", "icon": "mail", "order": 3, "visible_to": ["all"], "children": []},
        ],
    },
]

DEFAULT_SIDEBAR_WIDGETS = [
    {"type": "stats", "position": "top", "title": "Forum Stats", "settings": {"visibility": "all"}, "enabled": True, "order": 0},
    {"type": "profile", "position": "top", "title": "Your Profile", "settings": {"visibility": "user"}, "enabled": True, "order": 1},
    {"type": "top_users", "position": "middle", "title": "Top Members", "settings": {"items_count": 5, "visibility": "all"}, "enabled": True, "order": 0},
    {"type": "online_users", "position": "middle", "title": "Online Now", "settings": {"items_count": 10, "visibility": "all"}, "enabled": True, "order": 1},
    {"type": "recent_posts", "position": "bottom", "title": "Recent Activity", "settings": {"items_count": 5, "show_date": True, "show_author": True, "visibility": "all"}, "enabled": True, "order": 0},
]

DEFAULT_FRAMES = [
    {"slug": "neon-pulse", "name": "Neon Pulse", "description": "Cadru pulsatoriu cu efect neon", "image_url": "https://images.unsplash.com/photo-1518709268805-4e9042af2176?auto=format&fit=crop&w=200&q=80", "rarity": "common", "price_points": 50, "required_role": "user", "enabled": True},
    {"slug": "tactical-camo", "name": "Tactical Camo", "description": "Cadru tactical pentru pro-players", "image_url": "https://images.unsplash.com/photo-1542751371-adc38448a05e?auto=format&fit=crop&w=200&q=80", "rarity": "rare", "price_points": 250, "required_role": "user", "enabled": True},
    {"slug": "gold-ace", "name": "Gold Ace", "description": "Cadru aurit — doar pentru top fragger-i", "image_url": "https://images.unsplash.com/photo-1614624532983-4ce03382d63d?auto=format&fit=crop&w=200&q=80", "rarity": "epic", "price_points": 1000, "required_role": "user", "enabled": True},
    {"slug": "diamond-elite", "name": "Diamond Elite", "description": "Cadru cu diamante. Doar pentru veterani.", "image_url": "https://images.unsplash.com/photo-1611162616475-46b635cb6868?auto=format&fit=crop&w=200&q=80", "rarity": "legendary", "price_points": 5000, "required_role": "vip", "enabled": True},
    {"slug": "phoenix-flames", "name": "Phoenix Flames", "description": "Renaște ca o phoenix. VIP exclusive.", "image_url": "https://images.unsplash.com/photo-1518770660439-4636190af475?auto=format&fit=crop&w=200&q=80", "rarity": "mythic", "price_points": 15000, "required_role": "vip", "enabled": True},
]


# ---------------- ROUTER BUILDER ----------------

def build_idg_router_v2(db, get_current_user, require_admin, now_iso):
    api = APIRouter(prefix="/idg")

    def _slugify(s: str) -> str:
        s = s.lower().strip()
        s = re.sub(r"[^a-z0-9]+", "-", s)
        return s.strip("-")[:80] or uuid.uuid4().hex[:8]

    # ==================== MENUS ====================
    @api.get("/menus")
    async def list_menus(admin=Depends(require_admin)):
        return await db.idg_menus.find({}, {"_id": 0}).sort("location", 1).to_list(50)

    @api.get("/menus/public/{location}")
    async def public_menu(location: str):
        menu = await db.idg_menus.find_one({"location": location}, {"_id": 0})
        if not menu:
            return {"location": location, "items": []}
        return menu

    @api.post("/menus")
    async def create_menu(body: IDGMenu, admin=Depends(require_admin)):
        if await db.idg_menus.find_one({"location": body.location}):
            raise HTTPException(400, f"Menu for location '{body.location}' already exists. Use PATCH instead.")
        doc = body.model_dump()
        doc["id"] = uuid.uuid4().hex
        # ensure each item has an id
        for it in doc.get("items", []):
            if not it.get("id"): it["id"] = uuid.uuid4().hex
            for ch in it.get("children", []):
                if not ch.get("id"): ch["id"] = uuid.uuid4().hex
        doc["created_at"] = now_iso()
        await db.idg_menus.insert_one(doc)
        doc.pop("_id", None)
        return doc

    @api.patch("/menus/{menu_id}")
    async def update_menu(menu_id: str, body: IDGMenu, admin=Depends(require_admin)):
        updates = body.model_dump()
        # Never overwrite the document id from the request body (Pydantic defaults id=None)
        updates.pop("id", None)
        for it in updates.get("items", []):
            if not it.get("id"): it["id"] = uuid.uuid4().hex
            for ch in it.get("children", []):
                if not ch.get("id"): ch["id"] = uuid.uuid4().hex
        updates["updated_at"] = now_iso()
        result = await db.idg_menus.update_one({"id": menu_id}, {"$set": updates})
        if result.matched_count == 0:
            raise HTTPException(404, "Menu not found")
        return await db.idg_menus.find_one({"id": menu_id}, {"_id": 0})

    @api.delete("/menus/{menu_id}")
    async def delete_menu(menu_id: str, admin=Depends(require_admin)):
        await db.idg_menus.delete_one({"id": menu_id})
        return {"ok": True}

    # ==================== SIDEBAR WIDGETS ====================
    @api.get("/widgets")
    async def list_widgets(position: Optional[str] = None, admin=Depends(require_admin)):
        q = {} if not position else {"position": position}
        return await db.idg_widgets.find(q, {"_id": 0}).sort([("position", 1), ("order", 1)]).to_list(100)

    @api.get("/widgets/public")
    async def public_widgets():
        items = await db.idg_widgets.find({"enabled": True}, {"_id": 0}).sort([("position", 1), ("order", 1)]).to_list(100)
        return items

    @api.post("/widgets")
    async def create_widget(body: IDGSidebarWidget, admin=Depends(require_admin)):
        doc = body.model_dump()
        doc["id"] = uuid.uuid4().hex
        doc["created_at"] = now_iso()
        await db.idg_widgets.insert_one(doc)
        doc.pop("_id", None)
        return doc

    @api.put("/widgets/{widget_id}")
    @api.patch("/widgets/{widget_id}")
    async def update_widget(widget_id: str, body: Dict[str, Any], admin=Depends(require_admin)):
        updates = {k: v for k, v in body.items() if v is not None and k not in ["id", "_id"]}
        updates["updated_at"] = now_iso()
        result = await db.idg_widgets.update_one({"id": widget_id}, {"$set": updates})
        if result.matched_count == 0:
            raise HTTPException(404, "Widget not found")
        return await db.idg_widgets.find_one({"id": widget_id}, {"_id": 0})

    @api.delete("/widgets/{widget_id}")
    async def delete_widget(widget_id: str, admin=Depends(require_admin)):
        await db.idg_widgets.delete_one({"id": widget_id})
        return {"ok": True}

    @api.post("/widgets/reorder")
    async def reorder_widgets(items: List[Dict[str, Any]], admin=Depends(require_admin)):
        """items: [{id, position, order}]"""
        for it in items:
            await db.idg_widgets.update_one({"id": it["id"]}, {"$set": {"position": it.get("position", "middle"), "order": it.get("order", 0)}})
        return {"ok": True}

    # ==================== BLOCKS ====================
    @api.get("/blocks")
    async def list_blocks(position: Optional[str] = None, admin=Depends(require_admin)):
        q = {} if not position else {"position": position}
        return await db.idg_blocks.find(q, {"_id": 0}).sort([("position", 1), ("order", 1)]).to_list(100)

    @api.get("/blocks/public")
    async def public_blocks(position: Optional[str] = None, page: Optional[str] = None):
        q: Dict[str, Any] = {"enabled": True}
        if position: q["position"] = position
        items = await db.idg_blocks.find(q, {"_id": 0}).sort([("position", 1), ("order", 1)]).to_list(100)
        if page:
            items = [b for b in items if "all" in (b.get("show_on_pages") or []) or page in (b.get("show_on_pages") or [])]
        return items

    @api.post("/blocks")
    async def create_block(body: IDGBlock, admin=Depends(require_admin)):
        doc = body.model_dump()
        doc["id"] = uuid.uuid4().hex
        doc["created_at"] = now_iso()
        await db.idg_blocks.insert_one(doc)
        doc.pop("_id", None)
        return doc

    @api.patch("/blocks/{block_id}")
    async def update_block(block_id: str, body: IDGBlock, admin=Depends(require_admin)):
        updates = {k: v for k, v in body.model_dump().items() if v is not None and k != "id"}
        updates["updated_at"] = now_iso()
        result = await db.idg_blocks.update_one({"id": block_id}, {"$set": updates})
        if result.matched_count == 0:
            raise HTTPException(404, "Block not found")
        return await db.idg_blocks.find_one({"id": block_id}, {"_id": 0})

    @api.delete("/blocks/{block_id}")
    async def delete_block(block_id: str, admin=Depends(require_admin)):
        await db.idg_blocks.delete_one({"id": block_id})
        return {"ok": True}

    # ==================== PUBLIC THEME ENDPOINTS (no auth required) ====================
    @api.get("/theme/css")
    async def get_live_theme_css():
        """Public endpoint — serves the live index.css source without requiring auth."""
        import os
        BASE_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "frontend"))
        target = os.path.join(BASE_DIR, "src", "index.css")
        try:
            with open(target, "r", encoding="utf-8") as f:
                css_content = f.read()
            return FastResponse(content=css_content, media_type="text/css",
                                headers={"Cache-Control": "no-cache, no-store, must-revalidate",
                                         "Pragma": "no-cache", "Expires": "0"})
        except Exception as e:
            return FastResponse(content=f"/* Error loading theme: {e} */", media_type="text/css")

    # ==================== DESIGN FILES ====================
    class DesignFileUpdate(BaseModel):
        content: str

    @api.get("/design/files")
    async def get_design_file(filepath: str, admin=Depends(require_admin)):
        import os
        BASE_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "frontend"))
        if filepath == "css":
            target = os.path.join(BASE_DIR, "src", "index.css")
        elif filepath == "html":
            target = os.path.join(BASE_DIR, "public", "index.html")
        elif filepath == "app_css":
            target = os.path.join(BASE_DIR, "src", "App.css")
        elif filepath == "app_js":
            target = os.path.join(BASE_DIR, "src", "App.js")
        elif filepath == "header_js":
            target = os.path.join(BASE_DIR, "src", "components", "layout", "Header.js")
        elif filepath == "footer_js":
            target = os.path.join(BASE_DIR, "src", "components", "Footer.js")
        else:
            raise HTTPException(400, "Invalid file")
            
        try:
            with open(target, "r", encoding="utf-8") as f:
                return {"content": f.read()}
        except Exception as e:
            raise HTTPException(500, str(e))

    @api.post("/design/files")
    async def update_design_file(filepath: str, body: DesignFileUpdate, admin=Depends(require_admin)):
        import os
        import subprocess
        BASE_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "frontend"))
        if filepath == "css":
            target = os.path.join(BASE_DIR, "src", "index.css")
        elif filepath == "html":
            target = os.path.join(BASE_DIR, "public", "index.html")
        elif filepath == "app_css":
            target = os.path.join(BASE_DIR, "src", "App.css")
        elif filepath == "app_js":
            target = os.path.join(BASE_DIR, "src", "App.js")
        elif filepath == "header_js":
            target = os.path.join(BASE_DIR, "src", "components", "layout", "Header.js")
        elif filepath == "footer_js":
            target = os.path.join(BASE_DIR, "src", "components", "Footer.js")
        else:
            raise HTTPException(400, "Invalid file")
            
        try:
            with open(target, "w", encoding="utf-8") as f:
                f.write(body.content)
                
            # Try to trigger a build if deploy_fast.ps1 exists
            root_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
            deploy_script = os.path.join(root_dir, "deploy_fast.ps1")
            if os.path.exists(deploy_script):
                # Start deployment in background
                subprocess.Popen(["powershell", "-ExecutionPolicy", "Bypass", "-Command", f".\\{os.path.basename(deploy_script)}"], cwd=root_dir, shell=True)
                
            return {"ok": True}
        except Exception as e:
            raise HTTPException(500, str(e))

    # ==================== VIP FRAME MARKETPLACE ====================
    @api.get("/marketplace/frames")
    async def list_frames(user=Depends(get_current_user)):
        frames = await db.idg_frames.find({"enabled": True}, {"_id": 0}).sort([("order", 1), ("price_points", 1)]).to_list(300)
        # Attach owned-by-user state and apply 50% VIP discount calculation
        is_vip = bool(user and (user.get("is_vip") or user.get("role") in ("admin", "root")))
        owned_ids = set()
        if user:
            purchases = await db.idg_frame_purchases.find({"user_id": user["id"]}, {"_id": 0, "frame_id": 1}).to_list(500)
            for p in purchases:
                owned_ids.add(p["frame_id"])
                
        for f in frames:
            f["owned"] = f["id"] in owned_ids
            orig = f.get("price_points", 100)
            f["original_price"] = orig
            if is_vip:
                # 50% discount for VIP members!
                f["price_points"] = max(1, orig // 2)
                f["vip_discount"] = True
                f["discount_percent"] = 50
            else:
                f["vip_discount"] = False
                f["discount_percent"] = 0
        return frames

    @api.get("/admin/marketplace/frames")
    async def admin_list_frames(admin=Depends(require_admin)):
        return await db.idg_frames.find({}, {"_id": 0}).sort([("order", 1)]).to_list(200)

    @api.post("/admin/marketplace/frames")
    async def admin_create_frame(body: IDGFrame, admin=Depends(require_admin)):
        slug = _slugify(body.slug or body.name)
        if await db.idg_frames.find_one({"slug": slug}):
            raise HTTPException(400, "Slug already in use")
        doc = body.model_dump()
        doc["id"] = uuid.uuid4().hex
        doc["slug"] = slug
        doc["sales_count"] = 0
        doc["created_at"] = now_iso()
        await db.idg_frames.insert_one(doc)
        doc.pop("_id", None)
        return doc

    @api.patch("/admin/marketplace/frames/{frame_id}")
    async def admin_update_frame(frame_id: str, body: IDGFrame, admin=Depends(require_admin)):
        updates = {k: v for k, v in body.model_dump().items() if v is not None and k != "id"}
        updates["updated_at"] = now_iso()
        result = await db.idg_frames.update_one({"id": frame_id}, {"$set": updates})
        if result.matched_count == 0:
            raise HTTPException(404, "Frame not found")
        return await db.idg_frames.find_one({"id": frame_id}, {"_id": 0})

    @api.delete("/admin/marketplace/frames/{frame_id}")
    async def admin_delete_frame(frame_id: str, admin=Depends(require_admin)):
        await db.idg_frames.delete_one({"id": frame_id})
        return {"ok": True}

    @api.post("/marketplace/purchase")
    async def purchase_frame(body: IDGFramePurchaseRequest, user=Depends(get_current_user)):
        frame = await db.idg_frames.find_one({"id": body.frame_id, "enabled": True}, {"_id": 0})
        if not frame:
            raise HTTPException(404, "Frame not found")
        # Already owned?
        existing = await db.idg_frame_purchases.find_one({"user_id": user["id"], "frame_id": frame["id"]})
        if existing:
            raise HTTPException(400, "Already owned")
        # VIP gate
        if frame.get("required_role") == "vip" and not user.get("is_vip") and user.get("role") not in ("admin", "root"):
            raise HTTPException(403, "Această ramă necesită statut VIP")
        # Stock
        if frame.get("stock", -1) == 0:
            raise HTTPException(400, "Out of stock")
            
        # Price with 50% VIP discount
        is_vip = bool(user.get("is_vip") or user.get("role") in ("admin", "root"))
        orig_price = frame.get("price_points", 100)
        final_price = max(1, orig_price // 2) if is_vip else orig_price
        
        # Points check
        user_points = user.get("points", 0) or 0
        if user_points < final_price:
            raise HTTPException(400, f"Puncte IDG insuficiente. Ai nevoie de {final_price} IDG, ai {user_points} IDG.")
            
        # Deduct points
        await db.users.update_one(
            {"id": user["id"]},
            {"$inc": {"points": -final_price}}
        )
        # Create purchase record
        purchase = {
            "id": uuid.uuid4().hex,
            "user_id": user["id"],
            "frame_id": frame["id"],
            "frame_slug": frame["slug"],
            "frame_name": frame["name"],
            "price_paid": final_price,
            "created_at": now_iso(),
        }
        await db.idg_frame_purchases.insert_one(purchase)
        # Increment sales
        await db.idg_frames.update_one({"id": frame["id"]}, {"$inc": {"sales_count": 1}})
        if frame.get("stock", -1) > 0:
            await db.idg_frames.update_one({"id": frame["id"]}, {"$inc": {"stock": -1}})
            
        # Add frame to user inventory (so they can equip)
        await db.users.update_one(
            {"id": user["id"]},
            {"$addToSet": {"owned_frames": frame.get("image_filename") or frame["slug"]}}
        )
        remaining = user_points - final_price
        return {
            "ok": True,
            "purchased_frame": frame["name"],
            "price_paid": final_price,
            "remaining_points": remaining,
            "vip_discount_applied": is_vip
        }

    @api.get("/marketplace/my-purchases")
    async def my_purchases(user=Depends(get_current_user)):
        items = await db.idg_frame_purchases.find({"user_id": user["id"]}, {"_id": 0}).sort("created_at", -1).to_list(500)
        return items

    return api


# ==================== SEEDER ====================

async def seed_idg_v2(db, now_iso):
    """Seed default menus, sidebar widgets, and marketplace frames if missing."""
    if not await db.idg_menus.find_one({"location": "header"}):
        doc = {**DEFAULT_HEADER_MENU, "id": uuid.uuid4().hex, "created_at": now_iso()}
        for it in doc["items"]:
            it["id"] = uuid.uuid4().hex
        await db.idg_menus.insert_one(doc)

    for footer_menu in DEFAULT_FOOTER_MENUS:
        if not await db.idg_menus.find_one({"location": footer_menu["location"]}):
            doc = {**footer_menu, "id": uuid.uuid4().hex, "created_at": now_iso()}
            for it in doc["items"]:
                it["id"] = uuid.uuid4().hex
            await db.idg_menus.insert_one(doc)

    # Sidebar widgets
    if await db.idg_widgets.count_documents({}) == 0:
        for w in DEFAULT_SIDEBAR_WIDGETS:
            doc = {**w, "id": uuid.uuid4().hex, "created_at": now_iso()}
            await db.idg_widgets.insert_one(doc)

    # Marketplace frames
    for f in DEFAULT_FRAMES:
        if not await db.idg_frames.find_one({"slug": f["slug"]}):
            doc = {**f, "id": uuid.uuid4().hex, "stock": f.get("stock", -1), "sales_count": 0, "order": 0, "created_at": now_iso()}
            await db.idg_frames.insert_one(doc)
