<?php

namespace App\Http\Controllers\Admin;

use Carbon\Carbon;
use App\Utils\Helper;
use App\Models\Budget;
use App\Enums\UserType;
use App\Models\Company;
use App\Models\Category;
use App\Models\UnitType;
use App\Enums\ActionType;
use App\Models\Department;
use App\Models\BudgetDetail;
use Illuminate\Http\Request;
use App\Models\BudgetCategory;
use App\Enums\BudgetStatusType;
use App\Enums\BudgetType;
use App\Enums\OriginStatusType;
use Illuminate\Support\Facades\DB;
use App\Http\Controllers\Core\BaseResourceController;
use App\Http\Services\ApprovalLogService;
use App\Enums\ModuleType;

class BudgetController extends BaseResourceController
{
  protected $model = Budget::class;
  protected array $adminRole = [UserType::Director, UserType::Manager];

  protected function indexQuery($query, Request $request)
  {
    $query->when($request->filled("status"), fn($q) => $q->where("status", $request->status));
    $query->when($request->filled("type"), fn($q) => $q->where("type", $request->type));
    $query->withUserAction();
    $query->withPaidAmount();
    $query->filterDepartment();
    $query->with(["user:id,name", "reviewer:id,name"]);

    return $query;
  }
  protected function instanceData(\Illuminate\Support\Collection $instance)
  {
    return $instance;
  }

  protected function indexData(Request $request, $isFormData = true): array
  {
    $data = [];

    $budgetSumQuery = clone $this->mainQuery;
    $budgetSum = $budgetSumQuery->select([
      DB::raw("
            COALESCE(SUM(credit_amount), 0)  AS credit_total
        "),
      DB::raw("
            COALESCE(SUM(debit_amount), 0)  AS debit_total
        "),
      DB::raw("
            COALESCE(SUM(debit_amount), 0)
            -
            COALESCE(SUM(credit_amount), 0) AS remaining_total
        "),
    ])
      ->first();

    $paidTotal = \App\Models\Payment::whereIn('budget_id', (clone $this->mainQuery)->reorder()->select('budgets.id'))
      ->where('status', \App\Enums\PaymentStatusType::Paid)
      ->sum('amount');

    $budgetSum = [
      "credit_amount" => intval($budgetSum->credit_total ?? 0),
      "debit_amount" => intval($budgetSum->debit_total ?? 0),
      "remaining_amount" => intval($budgetSum->remaining_total ?? 0),
      "paid_amount" => intval($paidTotal),
    ];

    return [
      "budgetSum" => $budgetSum,
      "status" => BudgetStatusType::getValues(),
      "types" => BudgetType::getValues(),
    ];
  }

  protected function getPage(Request $request, $id = null): array
  {
    $page = [
      "name" => "budgets",
      "inertia" => "Admin/Budget",
      "label" => "Anggaran",
      "url" => "/admin/budgets",
      "fields" => Helper::getFormFields($this->validation($request)),
    ];

    return $page;
  }

  protected function validation(Request $request, $id = null): array
  {
    return [
      "validation" => [
        "code" => "nullable",
        "name" => "required|string",
        "type" => "required|string",
        "date" => "required|date",
        "year" => "required|string",
        "amount" => "required|integer",
        "description" => "nullable|string",
        "details" => "required|array",
      ],
      "detail_validation" => [
        "details.*.category_id" => "required",
        "details.*.items" => "required|array",
        "details.*.items.*.name" => "required|string",
        "details.*.items.*.price" => "required|integer",
        "details.*.items.*.price_total" => "required|integer",
        "details.*.items.*.description" => "nullable|string",

        "details.*.items.*.quantity_unit" => "array",
        "details.*.items.*.quantity_unit.*.quantity" => "required|integer",
        "details.*.items.*.quantity_unit.*.unit_type_id" => "required",
      ],
      "default" => [
        "status" => \App\Enums\BudgetStatusType::Draft,
        "date" => now()->format("Y-m-d"),
        "year" => now()->format("Y"),
      ],
    ];
  }

  protected function getFormData(Request $request, $model = null): array
  {
    $formData = [];
    $unit_types = UnitType::selectOptions()->get();
    $categories = Category::selectOptions()
      ->doesntHave('children')
      ->where('status', OriginStatusType::Active)
      ->orderByRaw("COALESCE(parent_id, id) ASC")
      ->orderBy("level", "ASC")
      ->orderBy("name", "ASC")
      ->get();

    if ($this->isEdit) {
      $this->modelInstance = Budget::with('budgetCategories.budgetDetails.quantityUnits')->find($this->modelInstance->id);
      $this->modelInstance->amount = $this->modelInstance->debit_amount;

      $this->modelInstance->details = $this->modelInstance->budgetCategories->map(function ($budgetCategory) {
        return [
          "category_id" => $budgetCategory->category_id,
          "items" => $budgetCategory->budgetDetails->map(function ($budgetDetail) {
            return [
              "id" => $budgetDetail->id,
              "name" => $budgetDetail->name,
              "price" => $budgetDetail->price,
              "price_total" => $budgetDetail->price_total,
              "description" => $budgetDetail->description,
              "quantity_unit" => $budgetDetail->quantityUnits->map(function ($quantityUnit) {
                return [
                  "quantity" => $quantityUnit->quantity,
                  "unit_type_id" => $quantityUnit->unit_type_id,
                ];
              }),
            ];
          }),
        ];
      });

      $this->modelInstance = collect($this->modelInstance)->except("budget_categories")->toArray();
    }

    return [
      "unit_types" => $unit_types,
      "categories" => $categories,
      "types" => BudgetType::getValues(),
      ...parent::getFormData($request, $model),
      ...$formData,
    ];
  }

  protected function getAuthorize(Request $request, $data = null, $action = ActionType::Read)
  {
    if ($data?->status?->value !== BudgetStatusType::Draft) {
      return Helper::redirectBack("error", "Hanya anggaran berstatus Draf yang dapat diubah atau dihapus");
    }
  }

  protected function beforeSave(array $validatedData, Request $request): array
  {
    $company = Company::active($request)->first();
    $validatedData['company_id'] = $company->id;
    $user = auth()->user();
    $validatedData['user_id'] = $user->id;

    $validatedData['year'] = Carbon::parse($validatedData['year'])?->year ?? Helper::getYearActive($request);
    $validatedData['debit_amount'] = 0;
    $validatedData['credit_amount'] = 0;
    $validatedData['status'] = \App\Enums\BudgetStatusType::Draft;

    if (empty($validatedData['code'])) {
      $department = Department::when($company, function ($query) use ($company) {
        $query->where('company_id', $company->id);
      })
        ->when($user->role !== UserType::PresidentDirector, function ($query) use ($user) {
          $query->whereHas('userDepartments', function ($q) use ($user) {
            $q->where('user_id', $user->id);
          });
        })
        ->first();

      $lastBudget = DB::table("budgets")->select("code")->where('company_id', $company->id)->latest("id")->whereNotNull('code')->first();
      $budgetCode = "$department->code/" . date('m') . "/" . date('Y');
      $lastBudget = DB::table("budgets")
        ->select("code")
        ->where('company_id', $company->id)
        ->latest("id")
        ->whereNotNull('code')
        ->first();

      $validatedData['code'] = "0001/$budgetCode";

      if ($lastBudget) {
        $lastCode = explode("/", $lastBudget->code);
        $lastCode = $lastCode[0];

        if ($lastCode) {
          $lastCode = str_pad(intval($lastCode) + 1, 4, "0", STR_PAD_LEFT);
          $validatedData['code'] = "$lastCode/$budgetCode";
        }
      }
    }

    unset($validatedData['amount']);
    unset($validatedData['details']);

    return $validatedData;
  }

  protected function afterSave($model, Request $request)
  {
    if (!$request->has('details') || !is_array($request->details))
      return;

    (new \App\Http\Services\BudgetDetailService())->saveDetails($request, $model);
  }

  protected function actionUpdate($id, Request $request)
  {
    if ($request->submit == '1')
      return $this->updateStatus(BudgetStatusType::Review, $request);
    if ($request->rejected == '1')
      return $this->updateStatus(BudgetStatusType::Rejected, $request);
    return null;
  }
  public function updateStatus($status, Request $request)
  {
    $allowedRoles = match ($status) {
      BudgetStatusType::Review => [UserType::Director, UserType::Manager, UserType::User],
      BudgetStatusType::Approved, BudgetStatusType::Rejected => [UserType::Director, UserType::Manager],
      default => []
    };

    if (!in_array(auth()->user()->role, $allowedRoles))
      return redirect()->back()->with("error", "Anda tidak dapat mengakses fitur ini");

    // Additional Validations
    $validate = [];
    if ($status === BudgetStatusType::Rejected) {
      $validate = $request->validate([
        "rejected_message" => "required|string",
      ]);
    }

    DB::beginTransaction();
    try {
      $modelClass = $this->model;
      $query = $modelClass::query();
      $query = $this->selectParams($query, $request);
      $budget = $query->first();

      // Limit Validation (Common across statuses)
      $limitValidation = $this->limitValidation($budget);
      if ($limitValidation instanceof \Illuminate\Http\RedirectResponse)
        return $limitValidation;

      // Status Updates
      $budget->status = $status;

      if (in_array($status, [BudgetStatusType::Approved, BudgetStatusType::Rejected])) {
        $budget->user_review_id = auth()->id();
        $budget->review_at = now()->toDateTimeString();
      }
      $note = "";
      if ($status === BudgetStatusType::Rejected) {
        $note = $validate["rejected_message"];
      }

      if ($note) {
        $budget->status_message = $note;
      }

      $budget->save();
      $budget->budgetDetails()->update([
        "status" => $status,
        "review_at" => now()->toDateTimeString(),
        "user_review_id" => auth()->id(),
      ]);

      (new ApprovalLogService())->create($request, $budget, ModuleType::Budget, null, [
        'note' => $note
      ]);

      DB::commit();
      return redirect(Helper::getRefurl(request()) ?? $this->page['url'])->with("success", "Update status {$this->page["label"]} berhasil");
    } catch (\Throwable $th) {
      DB::rollBack();
      return redirect()->back()->with("error", "Kesalahan Server. Update status {$this->page["label"]} gagal");
    }
  }

  protected function limitValidation($budget)
  {
    return true;
  }
}




