import csv
from datetime import date, timedelta
from decimal import Decimal

from django.contrib import messages
from django.db.models import F, Sum
from django.http import HttpResponse

from reportlab.lib import colors
from reportlab.lib.pagesizes import A4, landscape
from reportlab.lib.styles import getSampleStyleSheet
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle
from django.shortcuts import redirect, render
from django.utils import timezone

from accounts.branch_utils import get_branch_context, scope
from accounts.permissions import company_admin_required
from accounts.profit_utils import (
    financial_summary,
    group_profit,
    sale_line_financials,
    return_financials,
)
from expenses.models import Expense
from inventory.stock import stock_summary
from payroll.services import payroll_cost
from sales.models import Sale


def gc(request):
    return request.user.company


def _period(rt, today):
    if rt == "daily":
        sd = ed = today
        label = f"Daily - {today.strftime('%d %b %Y')}"
    elif rt == "weekly":
        sd = today - timedelta(days=today.weekday())
        ed = today
        label = f"Weekly - {sd.strftime('%d %b %Y')} to {ed.strftime('%d %b %Y')}"
    elif rt == "monthly":
        sd = today.replace(day=1)
        ed = today
        label = f"Monthly - {today.strftime('%B %Y')}"
    elif rt == "yearly":
        sd = today.replace(month=1, day=1)
        ed = today
        label = f"Yearly - {today.strftime('%Y')}"
    else:
        rt = "daily"
        sd = ed = today
        label = f"Daily - {today.strftime('%d %b %Y')}"
    return rt, sd, ed, label


def _build_trend(company, rt, sd, ed, branch=None):
    labels, revenue, gross_profit = [], [], []

    if rt == "yearly":
        cursor = sd.replace(day=1)
        while cursor <= ed:
            if cursor.month == 12:
                next_month = cursor.replace(year=cursor.year + 1, month=1, day=1)
            else:
                next_month = cursor.replace(month=cursor.month + 1, day=1)
            month_end = min(next_month - timedelta(days=1), ed)

            summary = financial_summary(company, cursor, month_end, branch)
            labels.append(cursor.strftime("%b %Y"))
            revenue.append(float(summary["revenue"]))
            gross_profit.append(float(summary["gross_profit"]))
            cursor = next_month
    else:
        cursor = sd
        while cursor <= ed:
            summary = financial_summary(company, cursor, cursor, branch)
            labels.append(cursor.strftime("%d %b"))
            revenue.append(float(summary["revenue"]))
            gross_profit.append(float(summary["gross_profit"]))
            cursor += timedelta(days=1)

    return labels, revenue, gross_profit


def _expenses_and_salaries(company, sd, ed, branch):
    expenses = scope(
        Expense.objects.filter(company=company, date__range=[sd, ed]), branch
    ).aggregate(total=Sum("amount"))["total"] or Decimal("0")
    salaries = payroll_cost(company, sd, ed, branch)
    return Decimal(expenses), Decimal(salaries)


def _branch_breakdown(company, ctx, sd, ed):
    rows = []
    for b in ctx.branches:
        s = financial_summary(company, sd, ed, b)
        exp, sal = _expenses_and_salaries(company, sd, ed, b)
        rows.append({
            "branch": b, "revenue": s["revenue"], "cogs": s["cogs"], "gross": s["gross_profit"],
            "expenses": exp, "salaries": sal, "net": s["gross_profit"] - exp - sal,
            "invoices": s["sales_count"],
        })
    return rows


@company_admin_required
def report_dashboard(request):
    company = gc(request)
    ctx = get_branch_context(request)
    branch = ctx.active
    today = timezone.now().date()
    rt, sd, ed, label = _period(request.GET.get("type", "daily"), today)

    summary = financial_summary(company, sd, ed, branch)

    expenses, salaries = _expenses_and_salaries(company, sd, ed, branch)

    net_profit = summary["gross_profit"] - expenses - salaries

    sales_rows = sale_line_financials(company, sd, ed, branch)
    return_rows = return_financials(company, sd, ed, branch)
    all_rows = sales_rows + return_rows

    brand_rows = group_profit(
        all_rows,
        lambda r: r["brand"].name if r["brand"] else "No Brand",
    )
    category_rows = group_profit(
        all_rows,
        lambda r: r["category"].name if r["category"] else "No Category",
    )
    product_rows = group_profit(
        all_rows,
        lambda r: r["product"].pk,
    )

    for row in product_rows:
        # Convert the grouping key into display data.
        product = next(
            (r["product"] for r in all_rows if r["product"].pk == row["key"]),
            None,
        )
        row["product_name"] = product.name if product else "Unknown"
        row["brand_name"] = product.brand.name if product and product.brand else "No Brand"

    product_rows.sort(key=lambda r: r["profit"], reverse=True)

    trend_labels, trend_revenue, trend_profit = _build_trend(
        company, rt, sd, ed, branch
    )

    stock = stock_summary(company, branch)

    recent_sales = (
        scope(
            Sale.objects.filter(company=company, date__range=[sd, ed]).select_related("customer"),
            branch,
        )[:20]
    )

    branch_rows = _branch_breakdown(company, ctx, sd, ed) if (branch is None and ctx.is_multi) else []

    context = {
        "label": label,
        "rt": rt,
        "sd": sd,
        "ed": ed,

        "ts": summary["revenue"],
        "cogs": summary["cogs"],
        "gp": summary["gross_profit"],
        "te": expenses,
        "salaries": salaries,
        "np": net_profit,
        "branch_rows": branch_rows,
        "tsc": summary["sales_count"],
        "return_revenue": summary["return_revenue"],

        "brand_rows": brand_rows,
        "category_rows": category_rows,
        "product_rows": product_rows,

        "trend_labels": trend_labels,
        "trend_revenue": trend_revenue,
        "trend_profit": trend_profit,

        "iv": stock["total_value"],
        "oos": len(stock["out_of_stock"]),
        "ls": len(stock["low_stock"]),

        "rs": recent_sales,
    }
    return render(request, "reports/report_dashboard.html", context)


@company_admin_required
def report_export_csv(request):
    company = gc(request)
    ctx = get_branch_context(request)
    today = timezone.now().date()
    rt, sd, ed, label = _period(request.GET.get("type", "daily"), today)
    label = f"{label} | {ctx.active_label}"

    rows = sale_line_financials(company, sd, ed, ctx.active) + return_financials(
        company, sd, ed, ctx.active
    )
    grouped = group_profit(
        rows,
        lambda r: r["product"].pk,
    )

    response = HttpResponse(content_type="text/csv; charset=utf-8")
    response["Content-Disposition"] = (
        f'attachment; filename="profit_statement_{rt}_{today:%Y%m%d}.csv"'
    )

    writer = csv.writer(response)
    writer.writerow([
        "Period", "Product", "Brand", "Quantity",
        "Average Buy Price", "Average Sell Price",
        "Revenue", "Cost of Goods Sold", "Profit",
    ])

    for row in sorted(grouped, key=lambda x: x["revenue"], reverse=True):
        product = next(
            (r["product"] for r in rows if r["product"].pk == row["key"]),
            None,
        )
        writer.writerow([
            label,
            product.name if product else "Unknown",
            product.brand.name if product and product.brand else "No Brand",
            f"{row['quantity']:.2f}",
            f"{row['avg_buy_price']:.2f}",
            f"{row['avg_sell_price']:.2f}",
            f"{row['revenue']:.2f}",
            f"{row['cost']:.2f}",
            f"{row['profit']:.2f}",
        ])

    return response


@company_admin_required
def report_export_pdf(request):
    company = gc(request)
    ctx = get_branch_context(request)
    today = timezone.now().date()
    rt, sd, ed, label = _period(request.GET.get("type", "daily"), today)
    label = f"{label} | Branch: {ctx.active_label}"
    summary = financial_summary(company, sd, ed, ctx.active)
    expenses, salaries = _expenses_and_salaries(company, sd, ed, ctx.active)
    net_profit = summary["gross_profit"] - expenses - salaries
    rows = sale_line_financials(company, sd, ed, ctx.active) + return_financials(company, sd, ed, ctx.active)
    grouped = group_profit(rows, lambda r: r["product"].pk)

    response = HttpResponse(content_type="application/pdf")
    response["Content-Disposition"] = f'attachment; filename="profit_statement_{rt}_{today:%Y%m%d}.pdf"'
    doc = SimpleDocTemplate(response, pagesize=landscape(A4), rightMargin=24, leftMargin=24, topMargin=24, bottomMargin=24)
    styles = getSampleStyleSheet()
    story = [
        Paragraph(f"{company.name} - Profit & Sales Statement", styles["Title"]),
        Paragraph(label, styles["Normal"]), Spacer(1, 10),
    ]
    summary_data = [
        ["Net Sales", "COGS", "Gross Profit", "Expenses", "Salaries", "Net Profit", "Invoices"],
        [f"{summary['revenue']:.2f}", f"{summary['cogs']:.2f}", f"{summary['gross_profit']:.2f}", f"{expenses:.2f}", f"{salaries:.2f}", f"{net_profit:.2f}", str(summary["sales_count"])],
    ]
    st = Table(summary_data, colWidths=[95]*7)
    st.setStyle(TableStyle([("BACKGROUND", (0,0), (-1,0), colors.HexColor("#1f2937")), ("TEXTCOLOR", (0,0), (-1,0), colors.white), ("GRID", (0,0), (-1,-1), .5, colors.grey), ("ALIGN", (0,0), (-1,-1), "CENTER")]))
    story += [st, Spacer(1, 14), Paragraph("Product-wise Profit", styles["Heading2"])]
    data = [["Product", "Brand", "Qty", "Avg Buy", "Avg Sell", "Revenue", "COGS", "Profit"]]
    for row in sorted(grouped, key=lambda x: x["profit"], reverse=True):
        product = next((r["product"] for r in rows if r["product"].pk == row["key"]), None)
        data.append([
            product.name if product else "Unknown",
            product.brand.name if product and product.brand else "No Brand",
            f"{row['quantity']:.2f}", f"{row['avg_buy_price']:.2f}", f"{row['avg_sell_price']:.2f}",
            f"{row['revenue']:.2f}", f"{row['cost']:.2f}", f"{row['profit']:.2f}",
        ])
    table = Table(data, repeatRows=1, colWidths=[170,110,60,80,80,85,85,85])
    table.setStyle(TableStyle([("BACKGROUND", (0,0), (-1,0), colors.HexColor("#111827")), ("TEXTCOLOR", (0,0), (-1,0), colors.white), ("GRID", (0,0), (-1,-1), .4, colors.grey), ("FONTSIZE", (0,0), (-1,-1), 8), ("ALIGN", (2,1), (-1,-1), "RIGHT")]))
    story.append(table)
    doc.build(story)
    return response
