from datetime import date
import logging
import uuid

from fastapi import APIRouter, Depends, Request, Response

from intranet.trip.src.api.auth import (
    has_transaction_read_perm,
    LazyManagersMapByTrip,
    is_yandex_coordinator,
    is_holding_coordinator,
)
from intranet.trip.src.api.depends import Pagination, get_unit_of_work
from intranet.trip.src.api.schemas import PaginatedResponse
from intranet.trip.src.api.schemas import (
    Transaction,
    TransactionLite,
    TransactionId,
    TransactionCreate,
    TransactionPartialUpdate,
    TransactionFilter,
    Balance,
    Expenses,
)
from intranet.trip.src import enums
from intranet.trip.src.unit_of_work import UnitOfWork
from intranet.trip.src.logic.billing import (
    get_transaction_actions,
    get_transaction_editable_fields,
    CreateTransactionAction,
    UpdateTransactionAction,
    DeleteTransactionAction,
)
from intranet.trip.src.exceptions import PermissionDenied


logger = logging.getLogger(__name__)
router = APIRouter()


@router.get(
    '/companies/{company_id}/transactions',
    status_code=200,
    response_model=PaginatedResponse[Transaction],
)
async def transaction_list(
    request: Request,
    uow: UnitOfWork = Depends(get_unit_of_work(read_only=True)),
    pagination=Depends(Pagination),
    transaction_filter=Depends(TransactionFilter),
):
    # TODO: BTRIP-3006 обработать случай ППЛ
    if not is_yandex_coordinator(request.state.user):
        raise PermissionDenied(log_message='User has no access to this handler')

    transactions, count = await uow.billing_transactions.get_list_and_count(fltr=transaction_filter)

    # TODO: BTRIP-3006
    managers_map = LazyManagersMapByTrip(uow, [])
    for transaction in transactions:
        transaction.editable_fields = await get_transaction_editable_fields(
            transaction=transaction,
            user=request.state.user,
            managers_map=managers_map,
        )

    return pagination.get_paginated_response(
        data=transactions,
        count=count,
    )


@router.get(
    '/companies/{company_id}/transactions/for_client',
    status_code=200,
    response_model=PaginatedResponse[TransactionLite],
)
async def transaction_list_for_client(
    request: Request,
    uow: UnitOfWork = Depends(get_unit_of_work(read_only=True)),
    pagination=Depends(Pagination),
    transaction_filter=Depends(TransactionFilter),
):
    company = await uow.companies.get_company(transaction_filter.company_id)

    if not is_holding_coordinator(request.state.user, company.holding_id):
        raise PermissionDenied(log_message='User has not access to lite transaction list')

    transactions, count = await uow.billing_transactions.get_list_and_count(fltr=transaction_filter)

    # TODO: BTRIP-3006
    managers_map = LazyManagersMapByTrip(uow, [])
    for transaction in transactions:
        transaction.editable_fields = await get_transaction_editable_fields(
            transaction=transaction,
            user=request.state.user,
            managers_map=managers_map,
        )

    return pagination.get_paginated_response(
        data=transactions,
        count=count,
    )


@router.get(
    '/companies/{company_id}/transactions/{transaction_id}',
    status_code=200,
    response_model=Transaction,
)
async def transaction_detail(
    request: Request,
    company_id: int,
    transaction_id: uuid.UUID,
    uow: UnitOfWork = Depends(get_unit_of_work(read_only=True)),
):
    # TODO: BTRIP-3006 обработать случай ППЛ
    if not is_yandex_coordinator(request.state.user):
        raise PermissionDenied(log_message='User has no access to this handler')

    transaction = await uow.billing_transactions.get_transaction(transaction_id)
    managers_map = LazyManagersMapByTrip(uow, trip_ids=[transaction.trip_id])

    if not await has_transaction_read_perm(uow, request.state.user, transaction, managers_map):
        raise PermissionDenied()

    transaction.actions = await get_transaction_actions(uow, request.state.user, transaction)
    transaction.editable_fields = await get_transaction_editable_fields(
        transaction=transaction,
        user=request.state.user,
        managers_map=managers_map,
    )

    return transaction


@router.get(
    '/companies/{company_id}/transactions/for_client/{transaction_id}',
    status_code=200,
    response_model=TransactionLite,
)
async def transaction_detail_for_client(
    request: Request,
    company_id: int,
    transaction_id: uuid.UUID,
    uow: UnitOfWork = Depends(get_unit_of_work(read_only=True)),
):
    transaction = await uow.billing_transactions.get_transaction(transaction_id)
    managers_map = LazyManagersMapByTrip(uow, trip_ids=[transaction.trip_id])

    if not await has_transaction_read_perm(uow, request.state.user, transaction, managers_map):
        raise PermissionDenied()

    transaction.actions = await get_transaction_actions(uow, request.state.user, transaction)
    transaction.editable_fields = await get_transaction_editable_fields(
        transaction=transaction,
        user=request.state.user,
        managers_map=managers_map,
    )

    return transaction


@router.post(
    '/companies/{company_id}/transactions',
    status_code=201,
    response_model=TransactionId,
)
async def transaction_create(
    request: Request,
    company_id: int,
    transaction: TransactionCreate,
    uow: UnitOfWork = Depends(get_unit_of_work()),
):
    action = CreateTransactionAction(
        uow=uow,
        user=request.state.user,
        transaction_create=transaction,
    )
    transaction_id = await action.check_and_execute()
    return {'transaction_id': transaction_id}


@router.patch(
    '/companies/{company_id}/transactions/{transaction_id}',
    status_code=200,
    response_model=TransactionId,
)
async def transaction_update(
    request: Request,
    company_id: int,
    transaction_id: uuid.UUID,
    transaction_update: TransactionPartialUpdate,
    uow: UnitOfWork = Depends(get_unit_of_work()),
):
    action = await UpdateTransactionAction.init(
        uow=uow,
        user=request.state.user,
        transaction_id=transaction_id,
    )
    transaction_id = await action.check_and_execute(transaction_update=transaction_update)
    return {'transaction_id': transaction_id}


@router.delete(
    '/companies/{company_id}/transactions/{transaction_id}',
    status_code=204,
    response_class=Response,
)
async def transaction_delete(
    request: Request,
    company_id: int,
    transaction_id: uuid.UUID,
    uow: UnitOfWork = Depends(get_unit_of_work()),
):
    action = await DeleteTransactionAction.init(
        uow=uow,
        user=request.state.user,
        transaction_id=transaction_id,
    )
    await action.check_and_execute()


@router.get(
    '/companies/{company_id}/balance',
    status_code=200,
    response_model=Balance,
)
async def company_balance(
    request: Request,
    company_id: int,
    uow: UnitOfWork = Depends(get_unit_of_work(read_only=True)),
):
    company = await uow.companies.get_company(company_id=company_id)

    if not is_holding_coordinator(request.state.user, company.holding_id):
        raise PermissionDenied(log_message='User has no access to companies balance')

    expenses = await uow.billing_transactions.get_expenses(company_id=company_id)
    deposits = await uow.billing_deposits.get_deposits_sum(company_id=company_id)

    balance = deposits - expenses
    status = enums.BalanceStatus.ok if balance > 0 else enums.BalanceStatus.empty
    last_deposit_date = await uow.billing_deposits.get_last_deposit_date(company_id=company_id)

    return {
        'company_id': company_id,
        'amount': balance,
        'last_deposit_date': last_deposit_date,
        'status': status,
    }


@router.get(
    '/companies/{company_id}/expenses',
    status_code=200,
    response_model=Expenses,
)
async def company_expenses(
    request: Request,
    company_id: int,
    date_from: date,
    date_to: date,
    uow: UnitOfWork = Depends(get_unit_of_work(read_only=True)),
):
    company = await uow.companies.get_company(company_id=company_id)

    if not is_holding_coordinator(request.state.user, company.holding_id):
        raise PermissionDenied(log_message='User has no access to companies expenses')

    amount = await uow.billing_transactions.get_expenses(company_id, date_from, date_to)

    return {
        'company_id': company_id,
        'amount': amount,
        'from_date': date_from,
        'to_date': date_to,
    }
