from datetime import timedelta
import calendar
import json
from decimal import Decimal

from django.contrib import messages
from django.contrib.auth import authenticate, login, logout
from django.contrib.auth.decorators import login_required
from django.db.models import Count, F, Sum
from django.views.decorators.http import require_POST
from django.shortcuts import get_object_or_404, redirect, render
from django.utils.http import url_has_allowed_host_and_scheme
from django.utils import timezone

from .branch_utils import (
    ALL, SESSION_KEY, ensure_main_branch, get_branch_context, scope,
)
from .forms import (
    CompanyRegistrationForm, UserForm, BranchForm,
    BankAccountForm, MobileBankingForm, CompanyPaymentForm, MakePaymentForm,
)
from .models import (
    Company, CustomUser, Branch,
    PlatformBankAccount, PlatformMobileBanking, CompanyPayment,
)
from .permissions import (
    company_admin_required, superadmin_required, ADMIN_UNLOCK_SESSION,
)
from .profit_utils import financial_summary
from .tenant_utils import provision_company_database
from inventory.models import Product, BranchStock
from inventory.stock import stock_summary
from payroll.services import payroll_cost
from sales.models import Sale, SaleItem
from expenses.models import Expense


def _sale_profit_totals(company, start_date, end_date, branch=None):
    summary = financial_summary(company, start_date, end_date, branch)
    return summary["revenue"], summary["cogs"], summary["gross_profit"]


def home_redirect(request):
    if not request.user.is_authenticated:
        return redirect("login")
    if request.user.is_superadmin:
        return redirect("superadmin_dashboard")
    if request.user.role == "admin":
        return redirect("dashboard")
    return redirect("sales_dashboard")


def register_company(request):
    if request.method == "POST":
        form = CompanyRegistrationForm(request.POST, request.FILES)
        if form.is_valid():
            form.save()
            messages.success(request, "Registration successful! Waiting for approval.")
            return redirect("login")
    else:
        form = CompanyRegistrationForm()
    return render(request, "accounts/company_register.html", {"form": form})


def login_view(request):
    if request.user.is_authenticated:
        if request.user.is_superadmin:
            return redirect("superadmin_dashboard")
        if request.user.role == "admin":
            return redirect("dashboard")
        return redirect("sales_dashboard")

    if request.method == "POST":
        username = request.POST.get("username", "").strip()
        password = request.POST.get("password", "")
        user = authenticate(request, username=username, password=password)

        if user is not None:
            if user.is_superadmin:
                login(request, user)
                return redirect("superadmin_dashboard")

            if not user.company:
                messages.error(request, "Your account is not linked to a company.")
                return render(request, "registration/login.html")

            if not user.company.is_approved:
                messages.error(request, "Your company registration is still pending super admin approval.")
            elif not user.company.is_active:
                messages.error(request, "Your company's access has been paused by the administrator. Please contact support.")
            elif not user.company.is_provisioned:
                messages.error(request, "Your company's workspace is still being set up. Please try again shortly.")
            elif not user.is_active:
                messages.error(request, "Your account is inactive. Please contact your company administrator.")
            else:
                login(request, user)
                if user.role == "admin":
                    return redirect("dashboard")
                return redirect("sales_dashboard")
        else:
            messages.error(request, "Invalid username or password.")

    return render(request, "registration/login.html")


def logout_view(request):
    # POST is preferred; GET is retained for compatibility with old bookmarks.
    logout(request)
    return redirect("login")


@login_required
def admin_unlock(request):
    """Unlock protected company-admin areas for the current sales user.

    The seller never receives admin permissions. They only get a short-lived
    session gate after valid credentials of an active admin from the same
    company are supplied.
    """
    user = request.user
    if user.is_superadmin or user.role == "admin":
        return redirect(request.GET.get("next") or "dashboard")
    if user.role != "sales_person" or not user.company_id:
        messages.error(request, "This unlock page is only for company sales users.")
        return redirect("sales_dashboard")

    next_url = request.POST.get("next") or request.GET.get("next") or "/dashboard/"
    if not url_has_allowed_host_and_scheme(next_url, allowed_hosts={request.get_host()}, require_https=request.is_secure()):
        next_url = "/dashboard/"

    if request.method == "POST":
        admin_username = request.POST.get("admin_username", "").strip()
        admin_password = request.POST.get("admin_password", "")
        admin_user = authenticate(request, username=admin_username, password=admin_password)
        if (admin_user is not None and admin_user.is_active and admin_user.role == "admin"
                and admin_user.company_id == user.company_id):
            request.session[ADMIN_UNLOCK_SESSION] = str(user.company_id)
            request.session[f"{ADMIN_UNLOCK_SESSION}_at"] = timezone.now().isoformat()
            request.session.modified = True
            messages.success(request, "Admin area unlocked for 30 minutes.")
            return redirect(next_url)
        messages.error(request, "Invalid company admin credentials.")

    return render(request, "accounts/admin_unlock.html", {"next": next_url})


def _branch_comparison(company, ctx, month_start, today):
    """One row per active branch for the 'All Branches' dashboard."""
    rows = []
    for b in ctx.branches:
        today_rev, _, _ = _sale_profit_totals(company, today, today, b)
        month_rev, month_cogs, month_gross = _sale_profit_totals(company, month_start, today, b)
        expenses = scope(
            Expense.objects.filter(company=company, date__range=[month_start, today]), b
        ).aggregate(t=Sum("amount"))["t"] or Decimal("0")
        salaries = payroll_cost(company, month_start, today, b)
        rows.append({
            "branch": b,
            "today_sales": today_rev,
            "month_sales": month_rev,
            "gross_profit": month_gross,
            "expenses": expenses,
            "salaries": salaries,
            "net_profit": month_gross - Decimal(expenses) - Decimal(salaries),
        })
    return rows


@login_required
def dashboard(request):
    """Private company-admin dashboard. Sales representatives are redirected to POS."""
    user = request.user
    if user.is_superadmin:
        return redirect("superadmin_dashboard")
    if user.role not in {"admin", "sales_person"} or not user.company_id:
        return redirect("sales_dashboard")
    if user.role == "sales_person":
        from .permissions import _unlock_is_valid
        if not _unlock_is_valid(request):
            next_url = request.get_full_path()
            return redirect(f"/admin-unlock/?next={next_url}")

    company = user.company
    ctx = get_branch_context(request)
    branch = ctx.active            # None = every branch
    today = timezone.now().date()
    this_month_start = today.replace(day=1)

    stock = stock_summary(company, branch)
    total_products = Product.objects.filter(company=company, is_active=True).count()
    low_stock_products = len(stock["alert_products"])

    today_revenue, today_cogs, today_gross = _sale_profit_totals(company, today, today, branch)
    month_revenue, month_cogs, month_gross = _sale_profit_totals(
        company, this_month_start, today, branch
    )

    month_expenses = scope(
        Expense.objects.filter(company=company, date__range=[this_month_start, today]), branch
    ).aggregate(t=Sum("amount"))["t"] or Decimal("0")
    month_salaries = payroll_cost(company, this_month_start, today, branch)

    month_net_profit = month_gross - Decimal(month_expenses) - Decimal(month_salaries)

    today_sales_count = scope(Sale.objects.filter(company=company, date=today), branch).count()

    recent_sales = scope(
        Sale.objects.filter(company=company).select_related("customer"), branch
    )[:10]

    low_stock = stock["alert_products"][:5]

    chart_labels = []
    chart_sales = []
    chart_profit = []

    # Last six calendar months, including the current month.
    for i in range(5, -1, -1):
        anchor = today.replace(day=1)
        month_index = anchor.month - 1 - i
        year = anchor.year + month_index // 12
        month = month_index % 12 + 1
        ms = anchor.replace(year=year, month=month, day=1)
        if month == 12:
            me = ms.replace(year=year + 1, month=1, day=1) - timedelta(days=1)
        else:
            me = ms.replace(month=month + 1, day=1) - timedelta(days=1)

        rev, cost, gross = _sale_profit_totals(company, ms, min(me, today), branch)
        chart_labels.append(ms.strftime("%b %Y"))
        chart_sales.append(float(rev))
        chart_profit.append(float(gross))

    now = timezone.now()
    cal = calendar.Calendar()
    month_days = list(cal.itermonthdays(now.year, now.month))
    month_name = now.strftime("%B %Y")

    recent_activities = []
    for sale in scope(Sale.objects.filter(company=company), branch).order_by("-created_at")[:5]:
        recent_activities.append({
            "text": f"Sale {sale.invoice_number} - {sale.net_amount}",
            "time": sale.created_at.strftime("%I:%M %p"),
        })

    branch_rows = []
    if branch is None and ctx.is_multi:
        branch_rows = _branch_comparison(company, ctx, this_month_start, today)

    context = {
        "total_products": total_products,
        "low_stock_products": low_stock_products,
        "today_sales": today_revenue,
        "today_cogs": today_cogs,
        "today_profit": today_gross,
        "today_sales_count": today_sales_count,
        "month_sales": month_revenue,
        "month_cogs": month_cogs,
        "month_expenses": month_expenses,
        "month_salaries": month_salaries,
        "month_profit": month_net_profit,
        "month_gross_profit": month_gross,
        "recent_sales": recent_sales,
        "low_stock": low_stock,
        "chart_labels": json.dumps(chart_labels),
        "chart_sales": json.dumps(chart_sales),
        "chart_profit": json.dumps(chart_profit),
        "month_days": month_days,
        "month_name": month_name,
        "now": now,
        "recent_activities": recent_activities,
        "today": today,
        "branch_rows": branch_rows,
        "stock_value": stock["total_value"],
    }
    return render(request, "accounts/admin_dashboard.html", context)


@login_required
def superadmin_dashboard(request):
    if not request.user.is_superadmin:
        return redirect("dashboard")

    context = {
        "total_companies": Company.objects.count(),
        "pending_companies": Company.objects.filter(is_approved=False).count(),
        "approved_companies": Company.objects.filter(is_approved=True).count(),
        "total_users": CustomUser.objects.count(),
        "pending_list": Company.objects.filter(
            is_approved=False
        ).order_by("-created_at")[:10],
        "approved_list": Company.objects.filter(
            is_approved=True
        ).annotate(branch_count=Count("branches")).order_by("-created_at")[:10],
    }
    return render(request, "accounts/superadmin_dashboard.html", context)


@login_required
def approve_company(request, pk):
    if not request.user.is_superadmin:
        return redirect("dashboard")
    c = get_object_or_404(Company, pk=pk)
    c.is_approved = True
    c.save(update_fields=["is_approved"])
    ensure_main_branch(c)
    try:
        provision_company_database(c)
        messages.success(request, f"{c.name} approved and its database has been created.")
    except Exception as e:
        messages.warning(
            request,
            f"{c.name} approved, but the database could not be set up automatically ({e}). "
            "It will retry when the company is provisioned again.",
        )
    return redirect("superadmin_dashboard")


@login_required
def reject_company(request, pk):
    if not request.user.is_superadmin:
        return redirect("dashboard")
    company = get_object_or_404(Company, pk=pk)
    name = company.name
    Company.objects.filter(pk=pk)._raw_delete(using="default")
    messages.warning(request, f"{name} rejected.")
    return redirect("superadmin_dashboard")


@login_required
def pause_company(request, pk):
    if not request.user.is_superadmin:
        return redirect("dashboard")
    c = get_object_or_404(Company, pk=pk)
    c.is_active = False
    c.save(update_fields=["is_active"])
    messages.success(request, f"{c.name} has been paused. Its users can no longer log in.")
    return redirect("superadmin_dashboard")


@login_required
def resume_company(request, pk):
    if not request.user.is_superadmin:
        return redirect("dashboard")
    c = get_object_or_404(Company, pk=pk)
    c.is_active = True
    c.save(update_fields=["is_active"])
    messages.success(request, f"{c.name} has been resumed.")
    return redirect("superadmin_dashboard")


@company_admin_required
def user_list(request):
    users = CustomUser.objects.filter(
        company=request.user.company
    ).select_related("branch").order_by("-created_at")
    return render(request, "accounts/user_list.html", {"users": users})


@company_admin_required
def user_create(request):
    if request.method == "POST":
        form = UserForm(request.POST, company=request.user.company)
        if form.is_valid():
            user = form.save(commit=False)
            user.company = request.user.company
            if form.cleaned_data.get("password"):
                user.set_password(form.cleaned_data["password"])
            user.save()
            messages.success(request, "User created.")
            return redirect("user_list")
    else:
        form = UserForm(company=request.user.company)
    return render(request, "accounts/user_form.html", {
        "form": form, "title": "Create User"
    })


@company_admin_required
def user_update(request, pk):
    user = get_object_or_404(
        CustomUser, pk=pk, company=request.user.company
    )
    if request.method == "POST":
        form = UserForm(request.POST, instance=user, company=request.user.company)
        if form.is_valid():
            form.save()
            messages.success(request, "User updated.")
            return redirect("user_list")
    else:
        form = UserForm(instance=user, company=request.user.company)
    return render(request, "accounts/user_form.html", {
        "form": form, "title": "Update User"
    })


@company_admin_required
def user_delete(request, pk):
    user = get_object_or_404(
        CustomUser, pk=pk, company=request.user.company
    )
    if user != request.user:
        CustomUser.objects.filter(pk=pk)._raw_delete(using="default")
        messages.success(request, "User deleted.")
    else:
        messages.error(request, "Cannot delete yourself.")
    return redirect("user_list")


# ---------------------------------------------------------------------------
# Super Admin: Payment Dashboard (all registered companies, platform banking
# info and mobile banking info used to collect payments from them)
# ---------------------------------------------------------------------------

@superadmin_required
def payment_dashboard(request):
    companies = Company.objects.all().order_by('-created_at')

    payments_by_company = {}
    verified_totals = CompanyPayment.objects.filter(status="verified").values(
        "company_id"
    ).annotate(total=Sum("amount"))
    for row in verified_totals:
        payments_by_company[row['company_id']] = row['total'] or Decimal('0')

    for c in companies:
        c.total_paid = payments_by_company.get(c.id, Decimal('0'))

    context = {
        "companies": companies,
        "bank_accounts": PlatformBankAccount.objects.all(),
        "mobile_accounts": PlatformMobileBanking.objects.all(),
        "recent_payments": CompanyPayment.objects.select_related(
            "company", "bank_account", "mobile_account"
        )[:25],
        "total_collected": CompanyPayment.objects.filter(status="verified").aggregate(
            t=Sum("amount")
        )["t"] or Decimal("0"),
        "pending_payments": CompanyPayment.objects.filter(status="pending").count(),
    }
    return render(request, "accounts/payment_dashboard.html", context)


@superadmin_required
def bank_account_list(request):
    accounts_qs = PlatformBankAccount.objects.all()
    return render(request, "accounts/bank_account_list.html", {"accounts": accounts_qs})


@superadmin_required
def bank_account_create(request):
    if request.method == "POST":
        form = BankAccountForm(request.POST)
        if form.is_valid():
            form.save()
            messages.success(request, "Bank account added.")
            return redirect("payment_dashboard")
    else:
        form = BankAccountForm()
    return render(request, "accounts/bank_account_form.html", {"form": form, "title": "Add Bank Account"})


@superadmin_required
def bank_account_update(request, pk):
    account = get_object_or_404(PlatformBankAccount, pk=pk)
    if request.method == "POST":
        form = BankAccountForm(request.POST, instance=account)
        if form.is_valid():
            form.save()
            messages.success(request, "Bank account updated.")
            return redirect("payment_dashboard")
    else:
        form = BankAccountForm(instance=account)
    return render(request, "accounts/bank_account_form.html", {"form": form, "title": "Update Bank Account"})


@superadmin_required
def bank_account_delete(request, pk):
    account = get_object_or_404(PlatformBankAccount, pk=pk)
    account.delete()
    messages.success(request, "Bank account removed.")
    return redirect("payment_dashboard")


@superadmin_required
def mobile_banking_create(request):
    if request.method == "POST":
        form = MobileBankingForm(request.POST)
        if form.is_valid():
            form.save()
            messages.success(request, "Mobile banking number added.")
            return redirect("payment_dashboard")
    else:
        form = MobileBankingForm()
    return render(request, "accounts/mobile_banking_form.html", {"form": form, "title": "Add Mobile Banking Number"})


@superadmin_required
def mobile_banking_update(request, pk):
    account = get_object_or_404(PlatformMobileBanking, pk=pk)
    if request.method == "POST":
        form = MobileBankingForm(request.POST, instance=account)
        if form.is_valid():
            form.save()
            messages.success(request, "Mobile banking number updated.")
            return redirect("payment_dashboard")
    else:
        form = MobileBankingForm(instance=account)
    return render(request, "accounts/mobile_banking_form.html", {"form": form, "title": "Update Mobile Banking Number"})


@superadmin_required
def mobile_banking_delete(request, pk):
    account = get_object_or_404(PlatformMobileBanking, pk=pk)
    account.delete()
    messages.success(request, "Mobile banking number removed.")
    return redirect("payment_dashboard")


@superadmin_required
def company_payment_list(request, company_pk):
    company = get_object_or_404(Company, pk=company_pk)
    payments = CompanyPayment.objects.filter(company=company).select_related(
        "bank_account", "mobile_account"
    )
    total_verified = payments.filter(status="verified").aggregate(t=Sum("amount"))["t"] or Decimal("0")
    return render(request, "accounts/company_payment_list.html", {
        "company": company, "payments": payments, "total_verified": total_verified,
    })


@superadmin_required
def company_payment_create(request, company_pk):
    company = get_object_or_404(Company, pk=company_pk)
    if request.method == "POST":
        form = CompanyPaymentForm(request.POST)
        if form.is_valid():
            payment = form.save(commit=False)
            payment.company = company
            payment.recorded_by = request.user
            payment.save()
            messages.success(request, f"Payment recorded for {company.name}.")
            return redirect("company_payment_list", company_pk=company.pk)
    else:
        form = CompanyPaymentForm(initial={"payment_date": timezone.now().date()})
    return render(request, "accounts/company_payment_form.html", {
        "form": form, "company": company, "title": f"Record Payment - {company.name}",
    })


@superadmin_required
def company_payment_update(request, company_pk, pk):
    company = get_object_or_404(Company, pk=company_pk)
    payment = get_object_or_404(CompanyPayment, pk=pk, company=company)
    if request.method == "POST":
        form = CompanyPaymentForm(request.POST, instance=payment)
        if form.is_valid():
            form.save()
            messages.success(request, "Payment updated.")
            return redirect("company_payment_list", company_pk=company.pk)
    else:
        form = CompanyPaymentForm(instance=payment)
    return render(request, "accounts/company_payment_form.html", {
        "form": form, "company": company, "title": f"Update Payment - {company.name}",
    })


@superadmin_required
def company_payment_delete(request, company_pk, pk):
    company = get_object_or_404(Company, pk=company_pk)
    payment = get_object_or_404(CompanyPayment, pk=pk, company=company)
    payment.delete()
    messages.success(request, "Payment record deleted.")
    return redirect("company_payment_list", company_pk=company.pk)


# ---------------------------------------------------------------------------
# Company Admin: "Make Payment" page — shows the platform's banking and
# mobile banking information and lets the company submit proof of payment.
# ---------------------------------------------------------------------------

@company_admin_required
def make_payment(request):
    company = request.user.company

    if request.method == "POST":
        form = MakePaymentForm(request.POST)
        if form.is_valid():
            payment = form.save(commit=False)
            payment.company = company
            payment.status = "pending"
            payment.recorded_by = request.user
            payment.save()
            messages.success(
                request,
                "Your payment has been submitted and is pending verification by the super admin."
            )
            return redirect("make_payment")
    else:
        form = MakePaymentForm(initial={"payment_date": timezone.now().date()})

    context = {
        "form": form,
        "bank_accounts": PlatformBankAccount.objects.filter(is_active=True),
        "mobile_accounts": PlatformMobileBanking.objects.filter(is_active=True),
        "my_payments": CompanyPayment.objects.filter(company=company).select_related(
            "bank_account", "mobile_account"
        ),
        "total_verified": CompanyPayment.objects.filter(
            company=company, status="verified"
        ).aggregate(t=Sum("amount"))["t"] or Decimal("0"),
    }
    return render(request, "accounts/make_payment.html", context)



# ---------------------------------------------------------------------------
# Branches
# ---------------------------------------------------------------------------

@login_required
@require_POST
def switch_branch(request):
    """Company admin picks "All Branches" or one branch for every dashboard."""
    user = request.user
    ctx = get_branch_context(request)
    if ctx is None or not ctx.can_switch:
        messages.error(request, "Only the company administrator can switch branches.")
        return redirect("home")

    chosen = request.POST.get("branch", ALL)
    if chosen == ALL:
        request.session[SESSION_KEY] = ALL
        messages.info(request, "Showing all branches.")
    else:
        branch = ctx.get_active_branch(chosen)
        if branch is None:
            messages.error(request, "That branch was not found.")
        else:
            request.session[SESSION_KEY] = str(branch.pk)
            messages.info(request, f"Showing {branch.name} only.")

    next_url = request.POST.get("next") or "/dashboard/"
    if not url_has_allowed_host_and_scheme(
        next_url, allowed_hosts={request.get_host()}, require_https=request.is_secure()
    ):
        next_url = "/dashboard/"
    return redirect(next_url)


@company_admin_required
def branch_list(request):
    company = request.user.company
    ensure_main_branch(company)
    branches = list(
        Branch.objects.filter(company=company).annotate(user_count=Count("users"))
        .order_by("-is_main", "name")
    )
    return render(request, "accounts/branch_list.html", {"branches": branches})


@company_admin_required
def branch_create(request):
    company = request.user.company
    if request.method == "POST":
        form = BranchForm(request.POST, company=company)
        if form.is_valid():
            branch = form.save(commit=False)
            branch.company = company
            branch.is_main = False
            branch.save()
            messages.success(
                request,
                f"Branch '{branch.name}' created. Add stock through Purchase Entry, "
                "and assign staff/sales users to it.",
            )
            return redirect("branch_list")
    else:
        form = BranchForm(company=company, initial={"is_active": True})
    return render(request, "accounts/branch_form.html", {"form": form, "title": "Add Branch"})


@company_admin_required
def branch_update(request, pk):
    company = request.user.company
    branch = get_object_or_404(Branch, pk=pk, company=company)
    if request.method == "POST":
        form = BranchForm(request.POST, instance=branch, company=company)
        if form.is_valid():
            form.save()
            messages.success(request, "Branch updated.")
            return redirect("branch_list")
    else:
        form = BranchForm(instance=branch, company=company)
    return render(request, "accounts/branch_form.html", {"form": form, "title": f"Edit Branch - {branch.name}"})
