<?php

namespace App\Http\Controllers\Admin;

use App\Exports\SimpleArrayExport;
use App\Http\Controllers\Controller;
use App\Models\BinaryLedger;
use App\Models\Franchise;
use App\Models\Order;
use App\Models\RankHistory;
use App\Models\Withdrawal;
use App\Models\User;
use App\Models\WalletTransaction;
use Barryvdh\DomPDF\Facade\Pdf;
use Illuminate\Contracts\View\View;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Maatwebsite\Excel\Facades\Excel;

class ReportController extends Controller
{
    public function index(): View
    {
        return view('admin.reports.index');
    }

    public function joining(Request $request): View|\Symfony\Component\HttpFoundation\BinaryFileResponse
    {
        [$from, $to] = $this->dateRange($request);

        $rows = User::query()
            ->where('role', 'user')
            ->whereDate('created_at', '>=', $from)
            ->whereDate('created_at', '<=', $to)
            ->with('sponsor')
            ->latest('created_at')
            ->get()
            ->map(fn ($user) => [
                optional($user->created_at)->toDateString(),
                $user->member_code,
                $user->name,
                $user->mobile,
                $user->sponsor?->member_code,
                $user->status,
            ])
            ->all();

        return $this->exportOrView(
            $request,
            'admin.reports.table',
            'joining-report',
            'Joining Report',
            ['Date', 'Member Code', 'Name', 'Mobile', 'Sponsor', 'Status'],
            $rows,
            compact('from', 'to', 'rows')
        );
    }

    public function bv(Request $request): View|\Symfony\Component\HttpFoundation\BinaryFileResponse
    {
        [$from, $to] = $this->dateRange($request);

        $rows = BinaryLedger::query()
            ->whereDate('posted_at', '>=', $from)
            ->whereDate('posted_at', '<=', $to)
            ->whereIn('ledger_type', ['joining', 'repurchase', 'reversal_joining', 'reversal_repurchase'])
            ->selectRaw("DATE(posted_at) as report_date, COALESCE(SUM(CASE WHEN direction='credit' THEN units ELSE -units END), 0) as total_bv")
            ->groupBy('report_date')
            ->orderBy('report_date')
            ->get()
            ->map(fn ($row) => [$row->report_date, $row->total_bv])
            ->all();

        return $this->exportOrView(
            $request,
            'admin.reports.table',
            'bv-report',
            'BV Report',
            ['Date', 'Total BV'],
            $rows,
            compact('from', 'to', 'rows')
        );
    }

    public function income(Request $request): View|\Symfony\Component\HttpFoundation\BinaryFileResponse
    {
        [$from, $to] = $this->dateRange($request);

        $rows = WalletTransaction::query()
            ->where('wallet_type', 'income')
            ->where('direction', 'credit')
            ->whereDate('transacted_at', '>=', $from)
            ->whereDate('transacted_at', '<=', $to)
            ->with('user')
            ->latest('transacted_at')
            ->get()
            ->map(fn ($txn) => [
                optional($txn->transacted_at)->format('Y-m-d'),
                $txn->user?->member_code,
                $txn->user?->name,
                $txn->amount,
                $txn->source_type,
                $txn->display_reference ?? $txn->reference,
            ])
            ->all();

        return $this->exportOrView(
            $request,
            'admin.reports.table',
            'income-report',
            'Income Report',
            ['Date', 'Member', 'Name', 'Amount', 'Source', 'Reference'],
            $rows,
            compact('from', 'to', 'rows')
        );
    }

    public function payout(Request $request): View|\Symfony\Component\HttpFoundation\BinaryFileResponse
    {
        [$from, $to] = $this->dateRange($request);

        $rows = Withdrawal::query()
            ->whereDate('requested_at', '>=', $from)
            ->whereDate('requested_at', '<=', $to)
            ->with('user')
            ->latest('requested_at')
            ->get()
            ->map(fn ($wd) => [
                optional($wd->requested_at)->format('Y-m-d'),
                $wd->user?->member_code,
                $wd->amount,
                $wd->net_amount,
                $wd->status,
            ])
            ->all();

        return $this->exportOrView(
            $request,
            'admin.reports.table',
            'payout-report',
            'Payout Report',
            ['Date', 'Member', 'Amount', 'Net', 'Status'],
            $rows,
            compact('from', 'to', 'rows')
        );
    }

    public function productSales(Request $request): View|\Symfony\Component\HttpFoundation\BinaryFileResponse
    {
        [$from, $to] = $this->dateRange($request);

        $rows = DB::table('order_items as oi')
            ->join('orders as o', 'o.id', '=', 'oi.order_id')
            ->whereDate('o.approved_at', '>=', $from)
            ->whereDate('o.approved_at', '<=', $to)
            ->whereIn('o.status', ['approved', 'delivered', 'dispatched'])
            ->groupBy('oi.product_id', 'oi.product_name')
            ->select([
                'oi.product_name',
                DB::raw('SUM(oi.quantity) as total_qty'),
                DB::raw('SUM(oi.line_total) as total_sales'),
                DB::raw('SUM(oi.line_bv) as total_bv'),
            ])
            ->orderBy('oi.product_name')
            ->get()
            ->map(fn ($row) => [
                $row->product_name,
                $row->total_qty,
                $row->total_sales,
                $row->total_bv,
            ])
            ->all();

        return $this->exportOrView(
            $request,
            'admin.reports.table',
            'product-sales-report',
            'Product Sales Report',
            ['Product', 'Qty', 'Sales', 'BV'],
            $rows,
            compact('from', 'to', 'rows')
        );
    }

    public function franchiseSales(Request $request): View|\Symfony\Component\HttpFoundation\BinaryFileResponse
    {
        [$from, $to] = $this->dateRange($request);

        $rows = DB::table('orders as o')
            ->leftJoin('franchises as f', 'f.id', '=', 'o.franchise_id')
            ->where('o.order_type', 'franchise')
            ->whereDate('o.approved_at', '>=', $from)
            ->whereDate('o.approved_at', '<=', $to)
            ->whereIn('o.status', ['approved', 'delivered', 'dispatched'])
            ->groupBy('o.franchise_id', 'f.area')
            ->select([
                'o.franchise_id',
                'f.area',
                DB::raw('COUNT(o.id) as orders_count'),
                DB::raw('SUM(o.total) as total_sales'),
                DB::raw('SUM(o.total_bv) as total_bv'),
            ])
            ->orderByDesc('total_sales')
            ->get()
            ->map(fn ($row) => [
                $row->franchise_id,
                $row->area,
                $row->orders_count,
                $row->total_sales,
                $row->total_bv,
            ])
            ->all();

        return $this->exportOrView(
            $request,
            'admin.reports.table',
            'franchise-sales-report',
            'Franchise Sales Report',
            ['Franchise ID', 'Area', 'Orders', 'Sales', 'BV'],
            $rows,
            compact('from', 'to', 'rows')
        );
    }

    public function wallet(Request $request): View|\Symfony\Component\HttpFoundation\BinaryFileResponse
    {
        [$from, $to] = $this->dateRange($request);

        $rows = WalletTransaction::query()
            ->whereDate('transacted_at', '>=', $from)
            ->whereDate('transacted_at', '<=', $to)
            ->with('user')
            ->latest('transacted_at')
            ->get()
            ->map(fn ($txn) => [
                optional($txn->transacted_at)->format('Y-m-d'),
                $txn->user?->member_code,
                $txn->wallet_type,
                $txn->direction,
                $txn->amount,
                $txn->source_type,
                $txn->display_reference ?? $txn->reference,
            ])
            ->all();

        return $this->exportOrView(
            $request,
            'admin.reports.table',
            'wallet-transactions-report',
            'Wallet Transactions Report',
            ['Date', 'Member', 'Wallet', 'Direction', 'Amount', 'Source', 'Reference'],
            $rows,
            compact('from', 'to', 'rows')
        );
    }

    public function tax(Request $request): View|\Symfony\Component\HttpFoundation\BinaryFileResponse
    {
        [$from, $to] = $this->dateRange($request);

        $rows = Withdrawal::query()
            ->whereDate('requested_at', '>=', $from)
            ->whereDate('requested_at', '<=', $to)
            ->with('user')
            ->latest('requested_at')
            ->get()
            ->map(fn ($wd) => [
                optional($wd->requested_at)->format('Y-m-d'),
                $wd->user?->member_code,
                $wd->amount,
                $wd->tds_amount,
                $wd->charge_amount,
                $wd->net_amount,
                $wd->status,
            ])
            ->all();

        return $this->exportOrView(
            $request,
            'admin.reports.table',
            'tax-report',
            'Tax Report',
            ['Date', 'Member', 'Amount', 'TDS', 'Charges', 'Net', 'Status'],
            $rows,
            compact('from', 'to', 'rows')
        );
    }

    public function rank(Request $request): View|\Symfony\Component\HttpFoundation\BinaryFileResponse
    {
        [$from, $to] = $this->dateRange($request);

        $rows = RankHistory::query()
            ->whereDate('run_date', '>=', $from)
            ->whereDate('run_date', '<=', $to)
            ->with(['user', 'fromRank', 'toRank'])
            ->latest('run_date')
            ->get()
            ->map(fn ($row) => [
                optional($row->run_date)->format('Y-m-d'),
                $row->user?->member_code,
                $row->fromRank?->name ?? '-',
                $row->toRank?->name ?? '-',
                $row->transition_type,
            ])
            ->all();

        return $this->exportOrView(
            $request,
            'admin.reports.table',
            'rank-report',
            'Rank Report',
            ['Date', 'Member', 'From Rank', 'To Rank', 'Type'],
            $rows,
            compact('from', 'to', 'rows')
        );
    }

    public function teamGrowth(Request $request): View|\Symfony\Component\HttpFoundation\BinaryFileResponse
    {
        [$from, $to] = $this->dateRange($request);

        $rows = User::query()
            ->where('role', 'user')
            ->whereDate('created_at', '>=', $from)
            ->whereDate('created_at', '<=', $to)
            ->selectRaw('DATE(created_at) as report_date, COUNT(*) as total')
            ->groupBy('report_date')
            ->orderBy('report_date')
            ->get()
            ->map(fn ($row) => [$row->report_date, $row->total])
            ->all();

        return $this->exportOrView(
            $request,
            'admin.reports.table',
            'team-growth-report',
            'Team Growth Report',
            ['Date', 'New Members'],
            $rows,
            compact('from', 'to', 'rows')
        );
    }

    public function withdrawalTrends(Request $request): View|\Symfony\Component\HttpFoundation\BinaryFileResponse
    {
        [$from, $to] = $this->dateRange($request);

        $rows = Withdrawal::query()
            ->whereDate('requested_at', '>=', $from)
            ->whereDate('requested_at', '<=', $to)
            ->selectRaw('DATE(requested_at) as report_date, SUM(amount) as total')
            ->groupBy('report_date')
            ->orderBy('report_date')
            ->get()
            ->map(fn ($row) => [$row->report_date, $row->total])
            ->all();

        return $this->exportOrView(
            $request,
            'admin.reports.table',
            'withdrawal-trends-report',
            'Withdrawal Trends Report',
            ['Date', 'Amount'],
            $rows,
            compact('from', 'to', 'rows')
        );
    }

    private function dateRange(Request $request): array
    {
        $from = $request->input('from', now()->startOfMonth()->toDateString());
        $to = $request->input('to', now()->toDateString());

        return [$from, $to];
    }

    /**
     * @param  array<int, string>  $headings
     * @param  array<int, array<int, mixed>>  $rows
     * @param  array<string, mixed>  $viewData
     */
    private function exportOrView(Request $request, string $view, string $filename, string $title, array $headings, array $rows, array $viewData): View|\Symfony\Component\HttpFoundation\BinaryFileResponse
    {
        if ($request->get('export') === 'excel') {
            return Excel::download(new SimpleArrayExport($rows, $headings), $filename.'.xlsx');
        }

        if ($request->get('export') === 'pdf') {
            $pdf = Pdf::loadView('admin.reports.pdf', [
                'headings' => $headings,
                'rows' => $rows,
                'title' => $title,
            ]);

            return $pdf->download($filename.'.pdf');
        }

        return view($view, array_merge($viewData, [
            'title' => $title,
            'headings' => $headings,
            'rows' => $rows,
        ]));
    }
}
