"""Database seed script.

Populates the database with canonical roles and permissions required by the
application.  Designed to be idempotent -- safe to run multiple times.

Usage:
    python -m app.seed
"""

import logging
import uuid
from datetime import datetime

from app.database import SessionLocal
from app.models.permission import Role, Permission, RolePermission

logger = logging.getLogger(__name__)


# ------------------------------------------------------------------- #
#  Permission definitions
# ------------------------------------------------------------------- #

PERMISSIONS: list[dict] = [
    # house
    {"code": "house:read", "description": "View house details and listing", "resource": "house", "action": "read"},
    {"code": "house:update", "description": "Edit house metadata (title, address, etc.)", "resource": "house", "action": "update"},
    {"code": "house:delete", "description": "Delete a house and all its data", "resource": "house", "action": "delete"},
    {"code": "house:manage_members", "description": "Add or remove users from a house", "resource": "house", "action": "manage_members"},
    # panorama
    {"code": "panorama:upload", "description": "Upload new panorama images", "resource": "panorama", "action": "upload"},
    {"code": "panorama:read", "description": "View panorama images", "resource": "panorama", "action": "read"},
    {"code": "panorama:delete", "description": "Delete panorama images", "resource": "panorama", "action": "delete"},
    {"code": "panorama:reorder", "description": "Reorder panoramas within a room", "resource": "panorama", "action": "reorder"},
    # room
    {"code": "room:create", "description": "Create new rooms", "resource": "room", "action": "create"},
    {"code": "room:update", "description": "Edit room properties", "resource": "room", "action": "update"},
    {"code": "room:delete", "description": "Delete rooms", "resource": "room", "action": "delete"},
    {"code": "room:reorder", "description": "Reorder rooms within a floor", "resource": "room", "action": "reorder"},
    # hotspot
    {"code": "hotspot:create", "description": "Create navigation hotspots", "resource": "hotspot", "action": "create"},
    {"code": "hotspot:update", "description": "Edit hotspot properties", "resource": "hotspot", "action": "update"},
    {"code": "hotspot:delete", "description": "Delete hotspots", "resource": "hotspot", "action": "delete"},
    {"code": "hotspot:batch_approve", "description": "Batch approve or reject AI-generated hotspots", "resource": "hotspot", "action": "batch_approve"},
    # ai
    {"code": "ai:suggestion:read", "description": "View AI-generated suggestions", "resource": "ai", "action": "suggestion_read"},
    {"code": "ai:suggestion:approve", "description": "Approve AI suggestions", "resource": "ai", "action": "suggestion_approve"},
    {"code": "ai:suggestion:reject", "description": "Reject AI suggestions", "resource": "ai", "action": "suggestion_reject"},
    {"code": "ai:job:create", "description": "Submit new AI processing jobs", "resource": "ai", "action": "job_create"},
    # publish
    {"code": "publish:submit_review", "description": "Submit a version for review", "resource": "publish", "action": "submit_review"},
    {"code": "publish:review", "description": "Review and approve/reject a submitted version", "resource": "publish", "action": "review"},
    {"code": "publish:publish", "description": "Publish an approved version to production", "resource": "publish", "action": "publish"},
    {"code": "publish:rollback", "description": "Rollback a published version", "resource": "publish", "action": "rollback"},
    {"code": "publish:read_history", "description": "View version history", "resource": "publish", "action": "read_history"},
    # tour
    {"code": "tour:preview", "description": "Preview a house tour before publishing", "resource": "tour", "action": "preview"},
    {"code": "tour:view_published", "description": "View published house tours", "resource": "tour", "action": "view_published"},
]


# ------------------------------------------------------------------- #
#  Role definitions & permission mapping
# ------------------------------------------------------------------- #

ROLES: list[dict] = [
    {
        "name": "admin",
        "description": "System administrator with full access to all houses and features",
        "is_system": True,
        "permission_codes": [
            "house:read", "house:update", "house:delete", "house:manage_members",
            "panorama:upload", "panorama:read", "panorama:delete", "panorama:reorder",
            "room:create", "room:update", "room:delete", "room:reorder",
            "hotspot:create", "hotspot:update", "hotspot:delete", "hotspot:batch_approve",
            "ai:suggestion:read", "ai:suggestion:approve", "ai:suggestion:reject", "ai:job:create",
            "publish:submit_review", "publish:review", "publish:publish", "publish:rollback", "publish:read_history",
            "tour:preview", "tour:view_published",
        ],
    },
    {
        "name": "owner",
        "description": "House owner with full control over their own houses",
        "is_system": False,
        "permission_codes": [
            "house:read", "house:update", "house:delete", "house:manage_members",
            "panorama:upload", "panorama:read", "panorama:delete", "panorama:reorder",
            "room:create", "room:update", "room:delete", "room:reorder",
            "hotspot:create", "hotspot:update", "hotspot:delete",
            "ai:suggestion:read", "ai:suggestion:approve", "ai:suggestion:reject", "ai:job:create",
            "publish:submit_review", "publish:read_history",
            "tour:preview", "tour:view_published",
        ],
    },
    {
        "name": "editor",
        "description": "Can edit house content but cannot manage members or publish",
        "is_system": False,
        "permission_codes": [
            "house:read",
            "panorama:upload", "panorama:read", "panorama:reorder",
            "room:create", "room:update", "room:reorder",
            "hotspot:create", "hotspot:update",
            "ai:suggestion:read", "ai:job:create",
            "publish:submit_review", "publish:read_history",
            "tour:preview",
        ],
    },
    {
        "name": "reviewer",
        "description": "Can review AI suggestions and hotspots, and approve publish versions",
        "is_system": False,
        "permission_codes": [
            "house:read",
            "panorama:read",
            "hotspot:batch_approve",
            "ai:suggestion:read", "ai:suggestion:approve", "ai:suggestion:reject",
            "publish:review", "publish:read_history",
            "tour:preview", "tour:view_published",
        ],
    },
    {
        "name": "viewer",
        "description": "Read-only access to published content",
        "is_system": False,
        "permission_codes": [
            "house:read",
            "panorama:read",
            "tour:view_published",
        ],
    },
]


def seed_database() -> None:
    """Insert or update seed data. Idempotent."""
    db = SessionLocal()
    try:
        _seed_permissions(db)
        _seed_roles(db)
        db.commit()
        logger.info("Database seeded successfully.")
    except Exception:
        db.rollback()
        logger.exception("Seed failed, transaction rolled back.")
        raise
    finally:
        db.close()


def _seed_permissions(db) -> None:
    """Upsert all permissions."""
    existing = {p.code: p for p in db.query(Permission).all()}
    now = datetime.utcnow()

    for perm_data in PERMISSIONS:
        code = perm_data["code"]
        if code in existing:
            perm = existing[code]
            # update mutable fields
            perm.description = perm_data["description"]
            perm.resource = perm_data["resource"]
            perm.action = perm_data["action"]
        else:
            perm = Permission(
                id=str(uuid.uuid4()),
                code=code,
                description=perm_data["description"],
                resource=perm_data["resource"],
                action=perm_data["action"],
                created_at=now,
            )
            db.add(perm)
            logger.info("Created permission: %s", code)

    db.flush()


def _seed_roles(db) -> None:
    """Upsert all roles and their permission mappings."""
    perm_map = {p.code: p for p in db.query(Permission).all()}
    missing = set()
    for role_data in ROLES:
        for code in role_data["permission_codes"]:
            if code not in perm_map:
                missing.add(code)
    if missing:
        logger.warning(
            "Some permission codes referenced by roles are not yet in the DB: %s",
            sorted(missing),
        )

    existing_roles = {r.name: r for r in db.query(Role).all()}
    now = datetime.utcnow()

    for role_data in ROLES:
        name = role_data["name"]
        if name in existing_roles:
            role = existing_roles[name]
            role.description = role_data["description"]
            role.is_system = role_data["is_system"]
        else:
            role = Role(
                id=str(uuid.uuid4()),
                name=name,
                description=role_data["description"],
                is_system=role_data["is_system"],
                created_at=now,
            )
            db.add(role)
            logger.info("Created role: %s", name)

        db.flush()

        # sync role-permission mappings
        _sync_role_permissions(db, role, role_data["permission_codes"], perm_map)


def _sync_role_permissions(
    db, role: Role, allowed_codes: list[str], perm_map: dict
) -> None:
    """Ensure the role has exactly the permissions listed in *allowed_codes*."""
    existing_links = {
        rp.permission_id: rp
        for rp in db.query(RolePermission).filter(
            RolePermission.role_id == role.id
        ).all()
    }

    desired_perm_ids = {
        perm_map[code].id
        for code in allowed_codes
        if code in perm_map
    }

    # remove links that should no longer exist
    for perm_id, link in list(existing_links.items()):
        if perm_id not in desired_perm_ids:
            db.delete(link)

    # add missing links
    for perm_id in desired_perm_ids:
        if perm_id not in existing_links:
            link = RolePermission(
                id=str(uuid.uuid4()),
                role_id=role.id,
                permission_id=perm_id,
            )
            db.add(link)


if __name__ == "__main__":
    logging.basicConfig(
        level=logging.INFO,
        format="%(asctime)s  %(levelname)-8s  %(message)s",
    )
    seed_database()
