from fastapi import APIRouter, Depends, HTTPException, status, Query, Request
from sqlalchemy.orm import Session
from typing import Optional
from datetime import datetime
from ...shared.config.database import get_db
from ...shared.config.logging_config import get_logger
from ...security.middleware.auth import get_current_user, authorize_roles
from ...auth.models.user import User
from ..models.invoice import Invoice, InvoiceStatus
from ...bookings.models.booking import Booking
from ..services.invoice_service import InvoiceService
from ...shared.utils.role_helpers import can_access_all_invoices, can_create_invoices
from ...shared.utils.response_helpers import success_response
from ...shared.utils.request_helpers import get_request_id
from ..schemas.invoice import (
    CreateInvoiceRequest,
    UpdateInvoiceRequest,
    MarkInvoicePaidRequest
)

logger = get_logger(__name__)
router = APIRouter(prefix='/invoices', tags=['invoices'])

@router.get('/')
async def get_invoices(request: Request, booking_id: Optional[int]=Query(None), status_filter: Optional[str]=Query(None, alias='status'), page: int=Query(1, ge=1), limit: int=Query(10, ge=1, le=100), current_user: User=Depends(get_current_user), db: Session=Depends(get_db)):
    try:
        user_id = None if can_access_all_invoices(current_user, db) else current_user.id
        result = InvoiceService.get_invoices(db=db, user_id=user_id, booking_id=booking_id, status=status_filter, page=page, limit=limit)
        return success_response(data=result)
    except Exception as e:
        db.rollback()
        logger.error(f'Error fetching invoices: {str(e)}', exc_info=True)
        raise HTTPException(status_code=500, detail=str(e))

@router.get('/{id}')
async def get_invoice_by_id(id: int, current_user: User=Depends(get_current_user), db: Session=Depends(get_db)):
    try:
        invoice = InvoiceService.get_invoice(id, db)
        if not invoice:
            raise HTTPException(status_code=404, detail='Invoice not found')
        if not can_access_all_invoices(current_user, db) and invoice['user_id'] != current_user.id:
            raise HTTPException(status_code=403, detail='Forbidden')
        return success_response(data={'invoice': invoice})
    except HTTPException:
        raise
    except Exception as e:
        db.rollback()
        logger.error(f'Error fetching invoice by id: {str(e)}', exc_info=True)
        raise HTTPException(status_code=500, detail=str(e))

@router.post('/')
async def create_invoice(request: Request, invoice_data: CreateInvoiceRequest, current_user: User=Depends(get_current_user), db: Session=Depends(get_db)):
    try:
        if not can_create_invoices(current_user, db):
            raise HTTPException(status_code=403, detail='Forbidden')
        booking_id = invoice_data.booking_id
        booking = db.query(Booking).filter(Booking.id == booking_id).first()
        if not booking:
            raise HTTPException(status_code=404, detail='Booking not found')
        invoice_kwargs = {
            'company_name': invoice_data.company_name,
            'company_address': invoice_data.company_address,
            'company_phone': invoice_data.company_phone,
            'company_email': invoice_data.company_email,
            'company_tax_id': invoice_data.company_tax_id,
            'company_logo_url': invoice_data.company_logo_url,
            'customer_tax_id': invoice_data.customer_tax_id,
            'notes': invoice_data.notes,
            'terms_and_conditions': invoice_data.terms_and_conditions,
            'payment_instructions': invoice_data.payment_instructions
        }
        invoice_notes = invoice_kwargs.get('notes', '')
        if booking.promotion_code:
            promotion_note = f'Promotion Code: {booking.promotion_code}'
            invoice_notes = f'{promotion_note}\n{invoice_notes}'.strip() if invoice_notes else promotion_note
            invoice_kwargs['notes'] = invoice_notes
        request_id = get_request_id(request)
        invoice = InvoiceService.create_invoice_from_booking(
            booking_id=booking_id,
            db=db,
            created_by_id=current_user.id,
            tax_rate=invoice_data.tax_rate,
            discount_amount=invoice_data.discount_amount,
            due_days=invoice_data.due_days,
            request_id=request_id,
            **invoice_kwargs
        )
        return success_response(data={'invoice': invoice}, message='Invoice created successfully')
    except HTTPException:
        raise
    except ValueError as e:
        db.rollback()
        logger.error(f'Error creating invoice: {str(e)}', exc_info=True)
        raise HTTPException(status_code=400, detail=str(e))
    except Exception as e:
        db.rollback()
        logger.error(f'Error creating invoice: {str(e)}', exc_info=True)
        raise HTTPException(status_code=500, detail=str(e))

@router.put('/{id}')
async def update_invoice(request: Request, id: int, invoice_data: UpdateInvoiceRequest, current_user: User=Depends(authorize_roles('admin', 'accountant')), db: Session=Depends(get_db)):
    try:
        invoice = db.query(Invoice).filter(Invoice.id == id).first()
        if not invoice:
            raise HTTPException(status_code=404, detail='Invoice not found')
        request_id = get_request_id(request)
        invoice_dict = invoice_data.model_dump(exclude_unset=True)
        updated_invoice = InvoiceService.update_invoice(invoice_id=id, db=db, updated_by_id=current_user.id, request_id=request_id, **invoice_dict)
        return success_response(data={'invoice': updated_invoice}, message='Invoice updated successfully')
    except HTTPException:
        raise
    except ValueError as e:
        db.rollback()
        logger.error(f'Error updating invoice: {str(e)}', exc_info=True)
        raise HTTPException(status_code=400, detail=str(e))
    except Exception as e:
        db.rollback()
        logger.error(f'Error updating invoice: {str(e)}', exc_info=True)
        raise HTTPException(status_code=500, detail=str(e))

@router.post('/{id}/mark-paid')
async def mark_invoice_as_paid(request: Request, id: int, payment_data: MarkInvoicePaidRequest, current_user: User=Depends(authorize_roles('admin', 'accountant')), db: Session=Depends(get_db)):
    try:
        request_id = get_request_id(request)
        amount = payment_data.amount
        updated_invoice = InvoiceService.mark_invoice_as_paid(invoice_id=id, db=db, amount=amount, updated_by_id=current_user.id, request_id=request_id)
        return success_response(data={'invoice': updated_invoice}, message='Invoice marked as paid successfully')
    except HTTPException:
        raise
    except ValueError as e:
        db.rollback()
        logger.error(f'Error marking invoice as paid: {str(e)}', exc_info=True)
        raise HTTPException(status_code=400, detail=str(e))
    except Exception as e:
        db.rollback()
        logger.error(f'Error marking invoice as paid: {str(e)}', exc_info=True)
        raise HTTPException(status_code=500, detail=str(e))

@router.delete('/{id}')
async def delete_invoice(id: int, current_user: User=Depends(authorize_roles('admin')), db: Session=Depends(get_db)):
    try:
        invoice = db.query(Invoice).filter(Invoice.id == id).first()
        if not invoice:
            raise HTTPException(status_code=404, detail='Invoice not found')
        db.delete(invoice)
        db.commit()
        return success_response(message='Invoice deleted successfully')
    except HTTPException:
        raise
    except Exception as e:
        db.rollback()
        raise HTTPException(status_code=500, detail=str(e))

@router.get('/booking/{booking_id}')
async def get_invoices_by_booking(booking_id: int, current_user: User=Depends(get_current_user), db: Session=Depends(get_db)):
    try:
        booking = db.query(Booking).filter(Booking.id == booking_id).first()
        if not booking:
            raise HTTPException(status_code=404, detail='Booking not found')
        if not can_access_all_invoices(current_user, db) and booking.user_id != current_user.id:
            raise HTTPException(status_code=403, detail='Forbidden')
        result = InvoiceService.get_invoices(db=db, booking_id=booking_id)
        return success_response(data=result)
    except HTTPException:
        raise
    except Exception as e:
        db.rollback()
        logger.error(f'Error fetching invoices by booking: {str(e)}', exc_info=True)
        raise HTTPException(status_code=500, detail=str(e))

@router.post('/{id}/send-email')
async def send_invoice_email(request: Request, id: int, current_user: User=Depends(authorize_roles('admin', 'staff', 'accountant')), db: Session=Depends(get_db)):
    try:
        invoice = db.query(Invoice).filter(Invoice.id == id).first()
        if not invoice:
            raise HTTPException(status_code=404, detail='Invoice not found')
        
        from ..routes.booking_routes import _generate_invoice_email_html
        from ...auth.models.user import User as UserModel
        from ...shared.utils.mailer import send_email
        
        invoice_dict = InvoiceService.invoice_to_dict(invoice)
        invoice_html = _generate_invoice_email_html(invoice_dict, is_proforma=invoice.is_proforma)
        invoice_type = 'Proforma Invoice' if invoice.is_proforma else 'Invoice'
        
        user = db.query(UserModel).filter(UserModel.id == invoice.user_id).first()
        if not user:
            raise HTTPException(status_code=404, detail='User not found')
        
        await send_email(
            to=user.email,
            subject=f'{invoice_type} {invoice.invoice_number}',
            html=invoice_html
        )
        
        logger.info(f'{invoice_type} {invoice.invoice_number} sent to {user.email}')
        return success_response(message=f'{invoice_type} sent successfully to {user.email}')
    except HTTPException:
        raise
    except Exception as e:
        db.rollback()
        logger.error(f'Error sending invoice email: {str(e)}', exc_info=True)
        raise HTTPException(status_code=500, detail=str(e))