<?php

namespace Modules\Accounting\Services;

use App\Models\ActivityLogModel;
use App\Models\FiscalYearModel;
use App\Models\JournalEntryModel;

class FiscalYearService
{
    protected FiscalYearModel $periods;
    protected JournalEntryModel $journals;
    protected ActivityLogModel $activity;

    public function __construct()
    {
        $this->periods  = new FiscalYearModel();
        $this->journals = new JournalEntryModel();
        $this->activity = new ActivityLogModel();
    }

    public function create(array $input, ?int $actingUserId): array
    {
        $code = strtoupper(trim($input['code'] ?? ''));
        if ($this->periods->isCodeTaken($code)) {
            return ['success' => false, 'errors' => ['code' => 'That period code is already in use.']];
        }

        $dateError = $this->validateDates($input['start_date'] ?? '', $input['end_date'] ?? '');
        if ($dateError) {
            return ['success' => false, 'errors' => ['end_date' => $dateError]];
        }

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

        if ((int) $data['is_current'] === 1) {
            $this->periods->clearCurrent();
        }

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

        $id = (int) $this->periods->getInsertID();
        $this->activity->log('fiscal_years', 'created', $actingUserId, "Created financial period #{$id} ({$data['code']})", (string) $id);

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

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

        if ($period['status'] === 'closed') {
            return ['success' => false, 'errors' => ['status' => 'Closed periods cannot be edited. Re-open first if needed.']];
        }

        $code = strtoupper(trim($input['code'] ?? ''));
        if ($this->periods->isCodeTaken($code, $id)) {
            return ['success' => false, 'errors' => ['code' => 'That period code is already in use.']];
        }

        $dateError = $this->validateDates($input['start_date'] ?? '', $input['end_date'] ?? '');
        if ($dateError) {
            return ['success' => false, 'errors' => ['end_date' => $dateError]];
        }

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

        if ((int) $data['is_current'] === 1) {
            $this->periods->clearCurrent($id);
        }

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

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

        return ['success' => true];
    }

    public function close(int $id, ?int $actingUserId): array
    {
        $period = $this->periods->find($id);
        if (! $period) {
            return ['success' => false, 'error' => 'Financial period not found.'];
        }
        if ($period['status'] === 'closed') {
            return ['success' => false, 'error' => 'Period is already closed.'];
        }

        $this->periods->update($id, [
            'status'     => 'closed',
            'is_current' => 0,
            'updated_by' => $actingUserId,
        ]);
        $this->activity->log('fiscal_years', 'closed', $actingUserId, "Closed financial period #{$id}", (string) $id);

        return ['success' => true];
    }

    public function reopen(int $id, ?int $actingUserId): array
    {
        $period = $this->periods->find($id);
        if (! $period) {
            return ['success' => false, 'error' => 'Financial period not found.'];
        }
        if ($period['status'] !== 'closed') {
            return ['success' => false, 'error' => 'Only closed periods can be reopened.'];
        }

        $this->periods->update($id, [
            'status'     => 'open',
            'updated_by' => $actingUserId,
        ]);
        $this->activity->log('fiscal_years', 'reopened', $actingUserId, "Reopened financial period #{$id}", (string) $id);

        return ['success' => true];
    }

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

        $used = $this->journals->where('fiscal_year_id', $id)->countAllResults();
        if ($used > 0) {
            return ['success' => false, 'error' => 'Cannot delete a period that has journal entries.'];
        }

        $this->periods->delete($id);
        $this->activity->log('fiscal_years', 'deleted', $actingUserId, "Deleted financial period #{$id}", (string) $id);

        return ['success' => true];
    }

    protected function validateDates(string $start, string $end): ?string
    {
        if ($start === '' || $end === '') {
            return 'Start and end dates are required.';
        }
        if (strtotime($end) < strtotime($start)) {
            return 'End date must be on or after the start date.';
        }

        return null;
    }

    protected function mapInput(array $input): array
    {
        return [
            'name'       => trim((string) $input['name']),
            'start_date' => $input['start_date'],
            'end_date'   => $input['end_date'],
            'status'     => ($input['status'] ?? 'open') === 'closed' ? 'closed' : 'open',
            'is_current' => ((string) ($input['is_current'] ?? '0') === '1') ? 1 : 0,
            'notes'      => ! empty($input['notes']) ? trim((string) $input['notes']) : null,
        ];
    }
}
