<?php

namespace App\Modules\Reports\Controllers;

use App\Http\Controllers\Controller;
use App\Http\Responses\ApiResponse;
use App\Modules\CRM\Models\Lead;
use App\Modules\Inventory\Models\Product;
use App\Modules\Inventory\Models\Purchase;
use App\Modules\Invoice\Models\Invoice;
use App\Modules\Invoice\Models\Payment;
use App\Modules\POS\Models\Sale;
use App\Modules\Tenant\Models\Company;
use Carbon\CarbonImmutable;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;

class ReportController extends Controller
{
    public function overview(Request $request): JsonResponse
    {
        $company = $this->currentCompany($request);
        [$startDate, $endDate] = $this->dateRange($request);

        $posSalesQuery = Sale::query()
            ->where('company_id', $company->id)
            ->where('status', 'completed')
            ->whereBetween('sale_date', [$startDate, $endDate]);
        $invoiceQuery = Invoice::query()
            ->where('company_id', $company->id)
            ->whereBetween('issue_date', [$startDate, $endDate]);
        $paymentQuery = Payment::query()
            ->where('company_id', $company->id)
            ->whereBetween('paid_at', [$startDate, $endDate]);
        $purchaseQuery = Purchase::query()
            ->where('company_id', $company->id)
            ->where('status', 'received')
            ->whereBetween('purchase_date', [$startDate, $endDate]);
        $leadQuery = Lead::query()
            ->where('company_id', $company->id);

        return ApiResponse::success([
            'period' => [
                'start_date' => $startDate,
                'end_date' => $endDate,
            ],
            'sales' => [
                'count' => (clone $posSalesQuery)->count(),
                'total' => $this->money((clone $posSalesQuery)->sum('total')),
                'paid' => $this->money((clone $posSalesQuery)->sum('paid_total')),
            ],
            'invoices' => [
                'count' => (clone $invoiceQuery)->count(),
                'total' => $this->money((clone $invoiceQuery)->sum('total')),
                'paid' => $this->money((clone $invoiceQuery)->sum('paid_total')),
                'due' => $this->money((clone $invoiceQuery)->sum('due_total')),
            ],
            'payments' => [
                'received' => $this->money((clone $paymentQuery)->sum('amount')),
            ],
            'purchases' => [
                'count' => (clone $purchaseQuery)->count(),
                'total' => $this->money((clone $purchaseQuery)->sum('total')),
            ],
            'inventory' => [
                'products' => Product::query()->where('company_id', $company->id)->count(),
                'low_stock' => Product::query()
                    ->where('company_id', $company->id)
                    ->whereColumn('current_stock', '<=', 'low_stock_threshold')
                    ->count(),
                'stock_value' => $this->money(Product::query()
                    ->where('company_id', $company->id)
                    ->selectRaw('COALESCE(SUM(current_stock * purchase_price), 0) as value')
                    ->value('value')),
            ],
            'crm' => [
                'total_leads' => (clone $leadQuery)->count(),
                'open_leads' => (clone $leadQuery)->where('status', 'open')->count(),
                'won_leads' => (clone $leadQuery)->where('stage', 'won')->count(),
                'pipeline_value' => $this->money((clone $leadQuery)->where('status', 'open')->sum('deal_value')),
            ],
        ]);
    }

    public function sales(Request $request): JsonResponse
    {
        $company = $this->currentCompany($request);
        [$startDate, $endDate] = $this->dateRange($request);

        $salesByDay = Sale::query()
            ->where('company_id', $company->id)
            ->where('status', 'completed')
            ->whereBetween('sale_date', [$startDate, $endDate])
            ->selectRaw('sale_date, COUNT(*) as count, COALESCE(SUM(total), 0) as total')
            ->groupBy('sale_date')
            ->orderBy('sale_date')
            ->get()
            ->map(fn ($row): array => [
                'date' => $row->sale_date->format('Y-m-d'),
                'count' => (int) $row->count,
                'total' => $this->money($row->total),
            ]);

        $paymentMethods = Sale::query()
            ->where('company_id', $company->id)
            ->where('status', 'completed')
            ->whereBetween('sale_date', [$startDate, $endDate])
            ->selectRaw('payment_method, COUNT(*) as count, COALESCE(SUM(total), 0) as total')
            ->groupBy('payment_method')
            ->orderByDesc('total')
            ->get()
            ->map(fn ($row): array => [
                'payment_method' => $row->payment_method,
                'count' => (int) $row->count,
                'total' => $this->money($row->total),
            ]);

        return ApiResponse::success([
            'period' => [
                'start_date' => $startDate,
                'end_date' => $endDate,
            ],
            'by_day' => $salesByDay,
            'payment_methods' => $paymentMethods,
        ]);
    }

    public function inventory(Request $request): JsonResponse
    {
        $company = $this->currentCompany($request);

        $products = Product::query()
            ->where('company_id', $company->id)
            ->orderBy('name')
            ->get();

        return ApiResponse::success([
            'summary' => [
                'products' => $products->count(),
                'low_stock' => $products->filter(
                    fn (Product $product): bool => (float) $product->current_stock <= (float) $product->low_stock_threshold
                )->count(),
                'stock_value' => $this->money($products->sum(
                    fn (Product $product): float => (float) $product->current_stock * (float) $product->purchase_price
                )),
            ],
            'low_stock_products' => $products
                ->filter(fn (Product $product): bool => (float) $product->current_stock <= (float) $product->low_stock_threshold)
                ->values()
                ->map(fn (Product $product): array => [
                    'id' => $product->id,
                    'name' => $product->name,
                    'sku' => $product->sku,
                    'current_stock' => $product->current_stock,
                    'low_stock_threshold' => $product->low_stock_threshold,
                    'unit' => $product->unit,
                ]),
        ]);
    }

    public function crm(Request $request): JsonResponse
    {
        $company = $this->currentCompany($request);

        $stages = Lead::query()
            ->where('company_id', $company->id)
            ->selectRaw('stage, COUNT(*) as count, COALESCE(SUM(deal_value), 0) as value')
            ->groupBy('stage')
            ->orderBy('stage')
            ->get()
            ->map(fn ($row): array => [
                'stage' => $row->stage,
                'count' => (int) $row->count,
                'value' => $this->money($row->value),
            ]);

        $followUps = Lead::query()
            ->where('company_id', $company->id)
            ->where('status', 'open')
            ->whereNotNull('next_follow_up_at')
            ->orderBy('next_follow_up_at')
            ->limit(10)
            ->get()
            ->map(fn (Lead $lead): array => [
                'id' => $lead->id,
                'name' => $lead->name,
                'stage' => $lead->stage,
                'next_follow_up_at' => optional($lead->next_follow_up_at)->format('Y-m-d'),
            ]);

        return ApiResponse::success([
            'stages' => $stages,
            'upcoming_follow_ups' => $followUps,
        ]);
    }

    /**
     * @return array{0: string, 1: string}
     */
    private function dateRange(Request $request): array
    {
        $startDate = $request->query('start_date')
            ? CarbonImmutable::parse((string) $request->query('start_date'))->toDateString()
            : now()->startOfMonth()->toDateString();
        $endDate = $request->query('end_date')
            ? CarbonImmutable::parse((string) $request->query('end_date'))->toDateString()
            : now()->endOfMonth()->toDateString();

        return [$startDate, $endDate];
    }

    private function money(mixed $value): string
    {
        return number_format((float) $value, 2, '.', '');
    }

    private function currentCompany(Request $request): Company
    {
        return $request->attributes->get('currentCompany');
    }
}
