<?php

namespace Modules\Inventory\Services;

use App\Models\ActivityLogModel;
use App\Models\StockBalanceModel;
use App\Models\StockTransferLineModel;
use App\Models\StockTransferModel;

class StockTransferService
{
    protected StockTransferModel $transfers;
    protected StockTransferLineModel $lines;
    protected StockBalanceModel $balances;
    protected ActivityLogModel $activity;

    public function __construct()
    {
        $this->transfers = new StockTransferModel();
        $this->lines     = new StockTransferLineModel();
        $this->balances  = new StockBalanceModel();
        $this->activity  = new ActivityLogModel();
    }

    public function create(array $input, ?int $actingUserId): array
    {
        $from = (int) ($input['from_warehouse_id'] ?? 0);
        $to   = (int) ($input['to_warehouse_id'] ?? 0);

        if ($from <= 0 || $to <= 0) {
            return ['success' => false, 'errors' => ['from_warehouse_id' => 'Source and destination warehouses are required.']];
        }

        if ($from === $to) {
            return ['success' => false, 'errors' => ['to_warehouse_id' => 'Source and destination warehouses must differ.']];
        }

        $data = [
            'number'            => $this->transfers->nextNumber(),
            'transfer_date'     => $input['transfer_date'] ?? date('Y-m-d'),
            'from_warehouse_id' => $from,
            'to_warehouse_id'   => $to,
            'status'            => 'draft',
            'notes'             => $input['notes'] ?: null,
            'created_by'        => $actingUserId,
            'updated_by'        => $actingUserId,
        ];

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

        $id = $this->transfers->getInsertID();
        $this->lines->replaceLines($id, $input['lines'] ?? []);
        $this->activity->log('stock_transfers', 'created', $actingUserId, "Created stock transfer #{$id} ({$data['number']})", (string) $id);

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

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

        if ($transfer['status'] !== 'draft') {
            return ['success' => false, 'errors' => ['status' => 'Only draft transfers can be edited.']];
        }

        $from = (int) ($input['from_warehouse_id'] ?? 0);
        $to   = (int) ($input['to_warehouse_id'] ?? 0);

        if ($from <= 0 || $to <= 0) {
            return ['success' => false, 'errors' => ['from_warehouse_id' => 'Source and destination warehouses are required.']];
        }

        if ($from === $to) {
            return ['success' => false, 'errors' => ['to_warehouse_id' => 'Source and destination warehouses must differ.']];
        }

        $data = [
            'transfer_date'     => $input['transfer_date'] ?? $transfer['transfer_date'],
            'from_warehouse_id' => $from,
            'to_warehouse_id'   => $to,
            'notes'             => $input['notes'] ?: null,
            'updated_by'        => $actingUserId,
        ];

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

        $this->lines->replaceLines($id, $input['lines'] ?? []);
        $this->activity->log('stock_transfers', 'updated', $actingUserId, "Updated stock transfer #{$id}", (string) $id);

        return ['success' => true];
    }

    public function complete(int $id, ?int $actingUserId): array
    {
        $db = \Config\Database::connect();
        $db->transStart();

        $transfer = $this->transfers->find($id);
        if (! $transfer) {
            $db->transRollback();

            return ['success' => false, 'error' => 'Stock transfer not found.'];
        }

        if ($transfer['status'] !== 'draft') {
            $db->transRollback();

            return ['success' => false, 'error' => 'Only draft transfers can be completed.'];
        }

        $lines = $this->lines->getForTransfer($id);
        if (count($lines) === 0) {
            $db->transRollback();

            return ['success' => false, 'error' => 'Add at least one line before completing.'];
        }

        foreach ($lines as $line) {
            $out = $this->balances->adjustQuantity(
                (int) $line['product_id'],
                (int) $transfer['from_warehouse_id'],
                -1 * (float) $line['quantity'],
                $actingUserId
            );
            if (! $out['success']) {
                $db->transRollback();

                return ['success' => false, 'error' => ($line['product_name'] ?? 'Product') . ': ' . $out['error']];
            }

            $in = $this->balances->adjustQuantity(
                (int) $line['product_id'],
                (int) $transfer['to_warehouse_id'],
                (float) $line['quantity'],
                $actingUserId
            );
            if (! $in['success']) {
                $db->transRollback();

                return ['success' => false, 'error' => $in['error']];
            }
        }

        $this->transfers->update($id, [
            'status'       => 'completed',
            'completed_at' => date('Y-m-d H:i:s'),
            'completed_by' => $actingUserId,
            'updated_by'   => $actingUserId,
        ]);

        $db->transComplete();

        if (! $db->transStatus()) {
            return ['success' => false, 'error' => 'Failed to complete transfer.'];
        }

        $this->activity->log('stock_transfers', 'completed', $actingUserId, "Completed stock transfer #{$id}", (string) $id);

        return ['success' => true];
    }

    public function cancel(int $id, ?int $actingUserId): array
    {
        $transfer = $this->transfers->find($id);
        if (! $transfer) {
            return ['success' => false, 'error' => 'Stock transfer not found.'];
        }

        if ($transfer['status'] !== 'draft') {
            return ['success' => false, 'error' => 'Only draft transfers can be cancelled.'];
        }

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

        return ['success' => true];
    }

    public function delete(int $id, ?int $actingUserId): array
    {
        $transfer = $this->transfers->find($id);
        if (! $transfer) {
            return ['success' => false, 'error' => 'Stock transfer not found.'];
        }

        if ($transfer['status'] !== 'draft') {
            return ['success' => false, 'error' => 'Only draft transfers can be deleted.'];
        }

        $this->lines->where('stock_transfer_id', $id)->delete();
        $this->transfers->delete($id);
        $this->activity->log('stock_transfers', 'deleted', $actingUserId, "Deleted stock transfer #{$id}", (string) $id);

        return ['success' => true];
    }
}
