<?php

namespace Modules\Procurement\Services;

use App\Models\ActivityLogModel;
use App\Models\PurchaseRequestLineModel;
use App\Models\PurchaseRequestModel;
use App\Models\RfqLineModel;
use App\Models\RfqModel;
use App\Models\RfqSupplierModel;
use App\Models\SupplierModel;

class RfqService
{
    protected RfqModel $rfqs;
    protected RfqLineModel $lines;
    protected RfqSupplierModel $rfqSuppliers;
    protected PurchaseRequestModel $purchaseRequests;
    protected PurchaseRequestLineModel $prLines;
    protected SupplierModel $suppliers;
    protected ActivityLogModel $activity;

    public function __construct()
    {
        $this->rfqs             = new RfqModel();
        $this->lines            = new RfqLineModel();
        $this->rfqSuppliers     = new RfqSupplierModel();
        $this->purchaseRequests = new PurchaseRequestModel();
        $this->prLines          = new PurchaseRequestLineModel();
        $this->suppliers        = new SupplierModel();
        $this->activity         = new ActivityLogModel();
    }

    public function create(array $input, ?int $actingUserId): array
    {
        $supplierError = $this->validateSupplierIds($input['supplier_ids'] ?? []);
        if ($supplierError !== null) {
            return ['success' => false, 'errors' => ['supplier_ids' => $supplierError]];
        }

        $prError = $this->validatePurchaseRequestId($input['purchase_request_id'] ?? null);
        if ($prError !== null) {
            return ['success' => false, 'errors' => ['purchase_request_id' => $prError]];
        }

        $data = $this->mapInput($input);
        $data['number']     = $this->rfqs->nextNumber();
        $data['status']     = in_array($input['status'] ?? 'draft', ['draft', 'pending_approval'], true)
            ? ($input['status'] ?? 'draft')
            : 'draft';
        $data['created_by'] = $actingUserId;
        $data['updated_by'] = $actingUserId;

        if (! $this->rfqs->insert($data, true)) {
            return ['success' => false, 'errors' => $this->rfqs->errors()];
        }

        $id = (int) $this->rfqs->getInsertID();
        $result = $this->lines->replaceForRfq($id, $input['lines'] ?? []);
        $tax = (float) ($input['tax_amount'] ?? 0);
        $this->rfqs->update($id, [
            'subtotal'   => $result['subtotal'],
            'tax_amount' => $tax,
            'total'      => round($result['subtotal'] + $tax, 2),
        ]);

        $this->rfqSuppliers->replaceForRfq($id, $input['supplier_ids'] ?? []);

        $this->activity->log('rfqs', 'created', $actingUserId, "Created RFQ #{$id} ({$data['number']})", (string) $id);

        return ['success' => true, 'id' => $id];
    }

    public function createFromPurchaseRequest(int $prId, ?int $actingUserId): array
    {
        $pr = $this->purchaseRequests->find($prId);
        if (! $pr) {
            return ['success' => false, 'error' => 'Purchase request not found.'];
        }
        if ($pr['status'] !== 'approved') {
            return ['success' => false, 'error' => 'Only approved purchase requests can be converted to an RFQ.'];
        }

        $prLines = $this->prLines->getForPurchaseRequest($prId);
        $input = [
            'purchase_request_id' => $prId,
            'rfq_date'            => date('Y-m-d'),
            'tax_amount'          => $pr['tax_amount'],
            'notes'               => $pr['notes'],
            'status'              => 'draft',
            'lines'               => $prLines,
            'supplier_ids'        => [],
        ];

        $result = $this->create($input, $actingUserId);
        if ($result['success']) {
            $this->purchaseRequests->update($prId, [
                'status'     => 'converted',
                'updated_by' => $actingUserId,
            ]);
            $this->activity->log(
                'purchase_requests',
                'converted',
                $actingUserId,
                "Converted purchase request #{$prId} to RFQ #{$result['id']}",
                (string) $prId
            );
        }

        return $result;
    }

    public function update(int $id, array $input, ?int $actingUserId): array
    {
        $rfq = $this->rfqs->find($id);
        if (! $rfq) {
            return ['success' => false, 'errors' => ['id' => 'RFQ not found.']];
        }

        if (! in_array($rfq['status'], ['draft', 'rejected'], true)) {
            return ['success' => false, 'errors' => ['status' => 'Only draft or rejected RFQs can be edited.']];
        }

        $supplierError = $this->validateSupplierIds($input['supplier_ids'] ?? []);
        if ($supplierError !== null) {
            return ['success' => false, 'errors' => ['supplier_ids' => $supplierError]];
        }

        $prError = $this->validatePurchaseRequestId($input['purchase_request_id'] ?? null);
        if ($prError !== null) {
            return ['success' => false, 'errors' => ['purchase_request_id' => $prError]];
        }

        $data = $this->mapInput($input);
        $data['updated_by'] = $actingUserId;

        if (! $this->rfqs->update($id, $data)) {
            return ['success' => false, 'errors' => $this->rfqs->errors()];
        }

        $result = $this->lines->replaceForRfq($id, $input['lines'] ?? []);
        $tax = (float) ($input['tax_amount'] ?? 0);
        $this->rfqs->update($id, [
            'subtotal'   => $result['subtotal'],
            'tax_amount' => $tax,
            'total'      => round($result['subtotal'] + $tax, 2),
            'updated_by' => $actingUserId,
        ]);

        $this->rfqSuppliers->replaceForRfq($id, $input['supplier_ids'] ?? []);

        $this->activity->log('rfqs', 'updated', $actingUserId, "Updated RFQ #{$id}", (string) $id);

        return ['success' => true];
    }

    public function submitForApproval(int $id, ?int $actingUserId): array
    {
        $rfq = $this->rfqs->find($id);
        if (! $rfq) {
            return ['success' => false, 'error' => 'RFQ not found.'];
        }
        if (! in_array($rfq['status'], ['draft', 'rejected'], true)) {
            return ['success' => false, 'error' => 'Only draft or rejected RFQs can be submitted.'];
        }

        $this->rfqs->update($id, [
            'status'           => 'pending_approval',
            'rejection_reason' => null,
            'updated_by'       => $actingUserId,
        ]);
        $this->activity->log('rfqs', 'submitted', $actingUserId, "Submitted RFQ #{$id} for approval", (string) $id);

        return ['success' => true];
    }

    public function approve(int $id, ?int $actingUserId): array
    {
        $rfq = $this->rfqs->find($id);
        if (! $rfq) {
            return ['success' => false, 'error' => 'RFQ not found.'];
        }
        if ($rfq['status'] !== 'pending_approval') {
            return ['success' => false, 'error' => 'RFQ is not pending approval.'];
        }

        $this->rfqs->update($id, [
            'status'           => 'approved',
            'rejection_reason' => null,
            'approved_by'      => $actingUserId,
            'approved_at'      => date('Y-m-d H:i:s'),
            'updated_by'       => $actingUserId,
        ]);
        $this->activity->log('rfqs', 'approved', $actingUserId, "Approved RFQ #{$id}", (string) $id);

        return ['success' => true];
    }

    public function reject(int $id, string $reason, ?int $actingUserId): array
    {
        $rfq = $this->rfqs->find($id);
        if (! $rfq) {
            return ['success' => false, 'error' => 'RFQ not found.'];
        }
        if ($rfq['status'] !== 'pending_approval') {
            return ['success' => false, 'error' => 'RFQ is not pending approval.'];
        }

        $this->rfqs->update($id, [
            'status'           => 'rejected',
            'rejection_reason' => trim($reason) ?: 'Rejected',
            'updated_by'       => $actingUserId,
        ]);
        $this->activity->log('rfqs', 'rejected', $actingUserId, "Rejected RFQ #{$id}", (string) $id);

        return ['success' => true];
    }

    public function markSent(int $id, ?int $actingUserId): array
    {
        $rfq = $this->rfqs->find($id);
        if (! $rfq) {
            return ['success' => false, 'error' => 'RFQ not found.'];
        }
        if ($rfq['status'] !== 'approved') {
            return ['success' => false, 'error' => 'Only approved RFQs can be marked as sent.'];
        }

        $this->rfqs->update($id, ['status' => 'sent', 'updated_by' => $actingUserId]);
        $this->activity->log('rfqs', 'sent', $actingUserId, "Marked RFQ #{$id} as sent", (string) $id);

        return ['success' => true];
    }

    public function cancel(int $id, ?int $actingUserId): array
    {
        $rfq = $this->rfqs->find($id);
        if (! $rfq) {
            return ['success' => false, 'error' => 'RFQ not found.'];
        }
        if (in_array($rfq['status'], ['converted', 'cancelled'], true)) {
            return ['success' => false, 'error' => 'RFQ cannot be cancelled.'];
        }

        $this->rfqs->update($id, ['status' => 'cancelled', 'updated_by' => $actingUserId]);
        $this->activity->log('rfqs', 'cancelled', $actingUserId, "Cancelled RFQ #{$id}", (string) $id);

        return ['success' => true];
    }

    public function delete(int $id, ?int $actingUserId): array
    {
        $rfq = $this->rfqs->find($id);
        if (! $rfq) {
            return ['success' => false, 'error' => 'RFQ not found.'];
        }
        if (! in_array($rfq['status'], ['draft', 'cancelled', 'rejected'], true)) {
            return ['success' => false, 'error' => 'Only draft, rejected, or cancelled RFQs can be deleted.'];
        }

        $this->lines->where('rfq_id', $id)->delete();
        $this->rfqSuppliers->where('rfq_id', $id)->delete();
        $this->rfqs->delete($id);
        $this->activity->log('rfqs', 'deleted', $actingUserId, "Deleted RFQ #{$id}", (string) $id);

        return ['success' => true];
    }

    protected function mapInput(array $input): array
    {
        return [
            'purchase_request_id' => ! empty($input['purchase_request_id']) ? (int) $input['purchase_request_id'] : null,
            'rfq_date'            => $input['rfq_date'] ?? date('Y-m-d'),
            'valid_until'         => ! empty($input['valid_until']) ? $input['valid_until'] : null,
            'notes'               => $input['notes'] ?: null,
        ];
    }

    protected function validateSupplierIds(array $supplierIds): ?string
    {
        foreach ($supplierIds as $sid) {
            $id = (int) $sid;
            if ($id <= 0) {
                continue;
            }
            if (! $this->suppliers->find($id)) {
                return "Supplier #{$id} not found.";
            }
        }

        return null;
    }

    protected function validatePurchaseRequestId($prId): ?string
    {
        if ($prId === null || $prId === '' || (int) $prId <= 0) {
            return null;
        }
        if (! $this->purchaseRequests->find((int) $prId)) {
            return 'Purchase request not found.';
        }

        return null;
    }
}
