"""
Rutas para gestión de empresa
"""

import logging
import mimetypes
import os
import shutil
from typing import List, Optional
from pathlib import Path
from datetime import datetime

from fastapi import APIRouter, Depends, HTTPException, status, File, UploadFile
from fastapi.responses import FileResponse, Response
from sqlalchemy.orm import Session

from core.config import settings
from core.database import get_core_db
from core.s3_keys import company_certificate_key, company_logo_key
from core.storage_s3 import delete_object_if_exists, get_object_bytes, put_object_bytes
from core.security import (
    collect_company_ids_from_app_membership,
    collect_user_role_names,
    get_current_user,
    get_tenant_from_token,
    resolve_effective_tenant_id_from_user,
    validate_access_to_resource,
)
from .....common.tenant_crud_routes import TenantCRUDRoutes
from .dto import CompanyCreateDTO, CompanyResponseDTO, CompanyUpdateDTO
from .models import Company
from .service import CompanyService

# Configuración de directorios
UPLOAD_DIR = "uploads/companies"
ALLOWED_EXTENSIONS = {".jpg", ".jpeg", ".png", ".gif", ".webp"}
MAX_FILE_SIZE = 5 * 1024 * 1024  # 5MB

logger = logging.getLogger(__name__)


def _user_is_admin(current_user: dict) -> bool:
    return "admin" in collect_user_role_names(current_user)


def _assert_permission_any_company(
    db: Session,
    current_user: dict,
    permission_code: str,
) -> None:
    """
    Enforces a permission when endpoint has no explicit company_id param.
    """
    if _user_is_admin(current_user):
        return

    user_id = current_user.get("sub") or current_user.get("id")
    if not user_id:
        raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Permission denied")

    company_ids = collect_company_ids_from_app_membership(db, str(user_id))
    if not company_ids:
        raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Permission denied")

    from api.v1.modules.core.permissions.service import PermissionService

    permission_service = PermissionService(db)
    for company_id in company_ids:
        if permission_service.has_any_permission(str(user_id), int(company_id), [permission_code]):
            return

    raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Permission denied")


def _assert_permission_for_company(
    db: Session,
    company_id: int,
    current_user: dict,
    permission_code: str,
) -> int:
    return validate_access_to_resource(db, company_id, current_user, [permission_code])


def _resolve_tenant_id_int(current_user: dict) -> int:
    """Misma lógica que validate_access_to_resource: entero estable para BD y claves S3."""
    tid = get_tenant_from_token(current_user)
    if tid is not None:
        return int(tid)
    raw = current_user.get("tenant_id")
    if isinstance(raw, list) and raw:
        raw = raw[0]
    if raw is None:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Tenant ID not found in user data",
        )
    try:
        return int(raw)
    except (TypeError, ValueError):
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Invalid tenant ID in token",
        )


def _is_s3_object_key(ref: Optional[str]) -> bool:
    return bool(ref and ref.startswith("tenants/"))


def _remove_stored_file(ref: str) -> None:
    if _is_s3_object_key(ref):
        delete_object_if_exists(ref)
    elif ref and os.path.isfile(ref):
        try:
            os.remove(ref)
        except OSError:
            pass

# Main router that includes base CRUD
router = APIRouter(prefix="/company")


@router.post(
    "",  # Se suma al prefix, queda POST /api/v1/a76/company
    response_model=CompanyResponseDTO,
    status_code=status.HTTP_201_CREATED,
    summary="Create a new company",
)
async def create_company(
    data: CompanyCreateDTO,
    db: Session = Depends(get_core_db),
    current_user: dict = Depends(get_current_user),
):
    _assert_permission_any_company(db, current_user, "cat_company.create")

    tenant_id = current_user.get("tenant_id")
    if not tenant_id:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Tenant ID not found in user data",
        )

    service = CompanyService(db)
    new_company = service.create_company_manually(data, tenant_id=tenant_id)
    return CompanyResponseDTO.model_validate(service.flatten_company_dto(new_company))


@router.get(
    "",  # GET /api/v1/a76/company with pagination
    response_model=dict,
    summary="Get companies with pagination",
)
async def list_companies(
    page: int = 1,
    page_size: int = 50,
    name: Optional[str] = None,
    rfc: Optional[str] = None,
    db: Session = Depends(get_core_db),
    current_user: dict = Depends(get_current_user),
):
    """Get paginated list of companies for current tenant with optional filters"""
    _assert_permission_any_company(db, current_user, "cat_company.view")

    tenant_id = current_user.get("tenant_id")
    if not tenant_id:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Tenant ID not found in user data",
        )

    skip = (page - 1) * page_size
    filters = {}
    if name:
        filters["name"] = name
    if rfc:
        filters["rfc"] = rfc

    service = CompanyService(db)
    items, total = service.get_all(
        db,
        tenant_id,
        company_id=0,  # Not used for companies
        skip=skip,
        limit=page_size,
        filters=filters if filters else None,
    )

    total_pages = (total + page_size - 1) // page_size

    return {
        "items": [
            CompanyResponseDTO.model_validate(service.flatten_company_dto(item))
            for item in items
        ],
        "total": total,
        "page": page,
        "page_size": page_size,
        "pages": total_pages,
    }


@router.get(
    "/my-companies",
    response_model=List[CompanyResponseDTO],
    summary="Get all companies for current tenant",
)
async def get_my_companies(
    db: Session = Depends(get_core_db),
    current_user: dict = Depends(get_current_user),
):
    """
    Compañías accesibles: tenant del token/Hub (si existe) unión con membership en BD
    (``user_company_roles``, ``user_company_permissions``, ``user_tenants``).
    Un usuario solo con roles de app y sin ``tenant_id`` en /auth/me sigue pudiendo
    listar sus compañías asignadas.
    """
    _assert_permission_any_company(db, current_user, "cat_company.view")

    user_id = current_user.get("sub") or current_user.get("id")
    tenant_id = resolve_effective_tenant_id_from_user(current_user)

    service = CompanyService(db)
    companies = service.get_companies_for_session_user(
        keycloak_user_id=str(user_id) if user_id else None,
        tenant_id_from_token=tenant_id,
    )

    return [
        CompanyResponseDTO.model_validate(service.flatten_company_dto(company))
        for company in companies
    ]

# ... existing code ...

@router.get(
    "/{company_id}",
    response_model=CompanyResponseDTO,
    summary="Get company by ID",
)
async def get_company(
    company_id: int,
    db: Session = Depends(get_core_db),
    current_user: dict = Depends(get_current_user),
):
    """Get a specific company by ID"""
    _assert_permission_for_company(db, company_id, current_user, "cat_company.view")

    tenant_id = current_user.get("tenant_id")
    if not tenant_id:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Tenant ID not found in user data",
        )

    service = CompanyService(db)
    company = CompanyService.get_by_id(db, company_id, tenant_id, 0)
    if not company:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Company not found",
        )

    return CompanyResponseDTO.model_validate(service.flatten_company_dto(company))


@router.put(
    "/{company_id}",
    response_model=CompanyResponseDTO,
    summary="Update company",
)
async def update_company(
    company_id: int,
    data: CompanyUpdateDTO,
    db: Session = Depends(get_core_db),
    current_user: dict = Depends(get_current_user),
):
    """Update a company"""
    _assert_permission_for_company(db, company_id, current_user, "cat_company.edit")

    tenant_id = current_user.get("tenant_id")
    if not tenant_id:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Tenant ID not found in user data",
        )

    service = CompanyService(db)
    updated_company = service.update(db, company_id, tenant_id, 0, data)
    if not updated_company:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Company not found",
        )

    return CompanyResponseDTO.model_validate(service.flatten_company_dto(updated_company))








@router.get(
    "/{company_id}/logo/image",
    summary="Get company logo image",
)

async def get_company_logo_image(
    company_id: int,
    db: Session = Depends(get_core_db),
    # Public endpoint to allow <img> tags to load the image without custom headers
):
    """Serve the company logo image file"""
    # Security: In a stricter environment, we would use a signed short-lived URL 
    # or cookie-based auth. For now, checking if company exists is sufficient.
    
    # We find the company ignoring tenant checks for the image serving
    # (Logos are generally considered semi-public assets in this context)
    company = db.query(Company).filter(Company.id == company_id).first()
    
    if not company or not company.logo:
        raise HTTPException(status_code=404, detail="Logo not found")

    if _is_s3_object_key(company.logo):
        try:
            data = get_object_bytes(company.logo)
        except Exception:
            raise HTTPException(status_code=404, detail="Logo file not found on server")
        media = mimetypes.guess_type(company.logo)[0] or "image/jpeg"
        return Response(content=data, media_type=media)

    file_path = Path(company.logo)
    if not file_path.exists():
        standard_path = Path(f"app_data/logos/{company_id}") / file_path.name
        if standard_path.exists():
            return FileResponse(standard_path)

        raise HTTPException(status_code=404, detail="Logo file not found on server")

    return FileResponse(file_path)


@router.delete(
    "/{company_id}",
    status_code=status.HTTP_204_NO_CONTENT,
    summary="Delete a company",
)
async def delete_company(
    company_id: int,
    db: Session = Depends(get_core_db),
    current_user: dict = Depends(get_current_user),
):
    """Delete a company"""
    _assert_permission_for_company(db, company_id, current_user, "cat_company.delete")

    tenant_id = current_user.get("tenant_id")
    if not tenant_id:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Tenant ID not found in user data",
        )

    success = CompanyService.delete(db, company_id, tenant_id, 0)
    if not success:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Company not found",
        )

    return None


@router.post(
    "/{company_id}/upload-logo",
    response_model=dict,
    summary="Upload company logo",
)
async def upload_company_logo(
    company_id: int,
    file: UploadFile = File(...),
    db: Session = Depends(get_core_db),
    current_user: dict = Depends(get_current_user),
):
    """Upload a logo for a company"""
    _assert_permission_for_company(db, company_id, current_user, "cat_company.edit")

    tenant_id = _resolve_tenant_id_int(current_user)

    # Validar que la empresa existe
    company = CompanyService.get_by_id(db, company_id, tenant_id, 0)
    if not company:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Company not found",
        )

    # Validar extensión
    file_ext = os.path.splitext(file.filename)[1].lower()
    if file_ext not in ALLOWED_EXTENSIONS:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail=f"File type not allowed. Allowed: {', '.join(ALLOWED_EXTENSIONS)}",
        )

    # Validar tamaño
    content = await file.read()
    if len(content) > MAX_FILE_SIZE:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail=f"File too large. Maximum size: {MAX_FILE_SIZE / 1024 / 1024}MB",
        )

    if company.logo:
        _remove_stored_file(company.logo)

    timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
    filename = f"logo_{company_id}_{timestamp}{file_ext}"

    try:
        if settings.use_s3_object_storage:
            ct = mimetypes.guess_type(filename)[0] or "image/jpeg"
            key = company_logo_key(tenant_id, company_id, filename)
            put_object_bytes(key, content, content_type=ct)
            logger.info(
                "Company logo stored in S3 key=%s bytes=%s",
                key,
                len(content),
            )
            file_path = key
        else:
            os.makedirs(UPLOAD_DIR, exist_ok=True)
            file_path = os.path.join(UPLOAD_DIR, filename)
            with open(file_path, "wb") as f:
                f.write(content)
    except Exception as e:
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"Error saving file: {str(e)}",
        )

    update_data = CompanyUpdateDTO(logo=file_path)
    service = CompanyService(db)
    updated_company = service.update(db, company_id, tenant_id, 0, update_data)

    return {
        "message": "Logo uploaded successfully",
        "logo_path": file_path,
        "company_id": company_id,
    }


@router.post(
    "/{company_id}/upload-certificate",
    response_model=dict,
    summary="Upload company certificate",
)
async def upload_company_certificate(
    company_id: int,
    certificate_type: str,
    file: UploadFile = File(...),
    db: Session = Depends(get_core_db),
    current_user: dict = Depends(get_current_user),
):
    """
    Upload a certificate for a company
    certificate_type: fiel_cer, fiel_key, cfdi_cert_cer, cfdi_cert_key, cancel_cer, cancel_key
    """
    _assert_permission_for_company(db, company_id, current_user, "cat_company.edit")

    tenant_id = _resolve_tenant_id_int(current_user)

    # Validar que la empresa existe
    service = CompanyService(db)
    company = service.get_by_id(db, company_id, tenant_id, 0)
    if not company:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Company not found",
        )
        
    # Validar tipo de certificado
    valid_types = [
        "fiel_cer", "fiel_key", 
        "cfdi_cert_cer", "cfdi_cert_key", 
        "cancel_cer", "cancel_key"
    ]
    if certificate_type not in valid_types:
          raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail=f"Invalid certificate type. Allowed: {', '.join(valid_types)}",
        )

    # Validar extensión
    file_ext = os.path.splitext(file.filename)[1].lower()
    allowed_exts = {".cer", ".key"}
    if file_ext not in allowed_exts:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail=f"File type not allowed. Allowed: {', '.join(allowed_exts)}",
        )
        
    # Validar correspondencia extensión vs tipo (simple check, respetando sufijo)
    ctype = (certificate_type or "").lower()
    if ctype.endswith("_cer") and file_ext != ".cer":
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="For this certificate type, file must be .cer",
        )
    if ctype.endswith("_key") and file_ext != ".key":
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="For this certificate type, file must be .key",
        )

    # Validar tamaño
    content = await file.read()
    if len(content) > MAX_FILE_SIZE:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail=f"File too large. Maximum size: {MAX_FILE_SIZE / 1024 / 1024}MB",
        )

    timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")

    try:
        if settings.use_s3_object_storage:
            key = company_certificate_key(
                tenant_id, company_id, certificate_type, timestamp, file_ext
            )
            ct = (
                "application/x-x509-ca-cert"
                if file_ext == ".cer"
                else "application/pkcs8"
            )
            put_object_bytes(key, content, content_type=ct)
            logger.info(
                "Company certificate stored in S3 key=%s bytes=%s",
                key,
                len(content),
            )
            file_path = key
        else:
            certs_dir = os.path.join(UPLOAD_DIR, str(company_id), "certificates")
            os.makedirs(certs_dir, exist_ok=True)
            filename = f"{certificate_type}_{timestamp}{file_ext}"
            file_path = os.path.join(certs_dir, filename)
            with open(file_path, "wb") as f:
                f.write(content)
    except Exception as e:
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"Error saving file: {str(e)}",
        )

    service.upload_certificate(company_id, certificate_type, file_path, tenant_id)

    return {
        "message": "Certificate uploaded successfully",
        "file_path": file_path,
        "certificate_type": certificate_type,
        "company_id": company_id,
    }
