from time import time
from typing import Optional

from fastapi import APIRouter
from pydantic import BaseModel

from app import mongo
from app.mongo import deals_collection, transactions_collection
from app.utils.response_util import response

router = APIRouter()


class ProfitParams(BaseModel):
    baseMarketplaceKey: str
    boardId: int
    clientId: int
    dateRange: list
    dealStatusId: int
    expenseTagId: int
    incomeTagId: int
    isCompletedOnly: bool
    managerId: int
    projectId: int
    groupTableBy: Optional[int] = None


def get_item_date(item: dict):
    if item.get("completedAt"):
        return item["completedAt"].split("T")[0]

    if item.get("createdAt"):
        return item["createdAt"].split("T")[0]

    if item.get("spentDate"):
        return item["spentDate"]


async def group_key(item: dict, group_by: int) -> Optional[str]:
    if group_by == 0:
        return get_item_date(item)

    elif group_by == 1:
        return item["clientName"]

    elif group_by == 2:
        project = await mongo.projects_collection.find_one({"id": item["board"]["projectId"]})
        return project["name"] if project else None

    elif group_by == 3:
        board = await mongo.boards_collection.find_one({"id": item["board"]["id"]})
        return board["name"] if board else None

    elif group_by == 4:
        return item["statusId"]

    elif group_by == 5:
        return item["shippingWarehouse"]

    elif group_by == 6:
        return item["baseMarketplace"]["name"]

    elif group_by == 7:
        manager = await mongo.users_collection.find_one({"id": item.get("managerId")})
        return manager["name"] if manager else None

    return None


async def get_profit_data(params: ProfitParams):
    start_datetime = f'{params.dateRange[0]}T00:00:00'
    end_datetime = f'{params.dateRange[1]}T23:59:59.999'

    deals_query = {
        "$and": [
            {
                "$or": [
                    {"isDeleted": False},
                    {"isDeleted": {"$exists": False}}
                ]
            },
            {
                "$or": [
                    {"completedAt": {"$gte": start_datetime, "$lte": end_datetime}},
                    {"$and": [
                        {"completedAt": None},
                        {"createdAt": {"$gte": start_datetime, "$lte": end_datetime}}
                    ]}
                ]
            }
        ]
    }

    if params.dealStatusId != -1:
        deals_query["statusId"] = params.dealStatusId

    elif params.boardId != -1:
        deals_query["board.id"] = params.boardId

    elif params.projectId != -1:
        deals_query["project.id"] = params.projectId

    if params.isCompletedOnly:
        deals_query["isCompleted"] = True

    client = None
    if params.clientId != -1:
        client = await mongo.clients_collection.find_one({"id": params.clientId}, {"_id": False})

    deals = []
    for deal in await deals_collection.find(deals_query, {"_id": False}).to_list(None):
        if params.baseMarketplaceKey != "all" and deal["baseMarketplace"]["key"] != params.baseMarketplaceKey:
            continue

        if client and deal["clientName"] != client["name"]:
            continue

        deals.append(deal)

    transactions_query = {
        "spentDate": {"$gte": params.dateRange[0], "$lte": params.dateRange[1]},
    }

    filter_income_tag = params.incomeTagId != -1
    filter_expense_tag = params.expenseTagId != -1

    transactions = await transactions_collection.find(transactions_query, {"_id": False}).to_list(None)

    if filter_income_tag or filter_expense_tag:
        def tag_matches(item, tag_id):
            return any(tag.get("id") == tag_id for tag in item.get("tags", []))

        filtered_transactions = []
        for transaction in transactions:
            if transaction["isIncome"] and filter_income_tag:
                if tag_matches(transaction, params.incomeTagId):
                    filtered_transactions.append(transaction)
            elif not transaction["isIncome"] and filter_expense_tag:
                if tag_matches(transaction, params.expenseTagId):
                    filtered_transactions.append(transaction)
            elif not filter_income_tag and not filter_expense_tag:
                filtered_transactions.append(transaction)
        transactions = filtered_transactions

    return deals, transactions


@router.post("/get-profit-chart-data", tags=["Profit"])
async def get_profit_chart_data(params: ProfitParams):
    start_time = time()

    deals, transactions = await get_profit_data(params)
    deals_total_prices = await mongo.get_deals_total_prices(*deals)
    grouped = {}

    for deal in deals:
        date = get_item_date(deal)
        if date not in grouped:
            grouped[date] = {
                "date": date,
                "revenue": 0,
                "expenses": 0,
                "profit": 0,
                "dealsCount": 0
            }

        grouped[date]["dealsCount"] += 1
        if deal.get("isCompleted") or deal.get("completedAt"):
            total_price = deals_total_prices.get(deal["id"])
            if total_price:
                grouped[date]["revenue"] += total_price
                grouped[date]["profit"] += total_price

    for transaction in transactions:
        date = transaction["spentDate"]
        if date not in grouped:
            grouped[date] = {
                "date": date,
                "revenue": 0,
                "expenses": 0,
                "profit": 0,
                "dealsCount": 0
            }

        if transaction["isIncome"]:
            grouped[date]["revenue"] += transaction["amount"]
            grouped[date]["profit"] += transaction["amount"]
        else:
            grouped[date]["expenses"] += transaction["amount"]
            grouped[date]["profit"] -= transaction["amount"]

    sorted_grouped_values = [value for _, value in sorted(grouped.items())]
    return response({"data": sorted_grouped_values}, start_time=start_time)


@router.post("/get-profit-table-data", tags=["Profit"])
async def get_profit_table_data(params: ProfitParams):
    start_time = time()

    deals, transactions = await get_profit_data(params)
    deals_total_prices = await mongo.get_deals_total_prices(*deals)
    grouped = {}

    for deal in deals:
        key = await group_key(deal, params.groupTableBy)
        if not key:
            continue

        if key not in grouped:
            grouped[key] = {
                "groupedValue": key,
                "revenue": 0,
                "expenses": 0,
                "profit": 0,
                "dealsCount": 0
            }

        grouped[key]["dealsCount"] += 1
        if deal.get("isCompleted") or deal.get("completedAt"):
            total_price = deals_total_prices.get(deal["id"])
            if total_price:
                grouped[key]["revenue"] += total_price
                grouped[key]["profit"] += total_price

    if params.groupTableBy == 0:
        for transaction in transactions:
            key = await group_key(transaction, params.groupTableBy)
            if not key:
                continue

            if key not in grouped:
                grouped[key] = {
                    "groupedValue": key,
                    "revenue": 0,
                    "expenses": 0,
                    "profit": 0,
                    "dealsCount": 0
                }

            if transaction["isIncome"]:
                grouped[key]["revenue"] += transaction["amount"]
                grouped[key]["profit"] += transaction["amount"]
            else:
                grouped[key]["expenses"] += transaction["amount"]
                grouped[key]["profit"] -= transaction["amount"]

    sorted_grouped_values = [value for _, value in sorted(grouped.items())]
    return response({"data": sorted_grouped_values}, start_time=start_time)
