<?php

namespace App\Services;

use App\Models\Finance\FundSchedule;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;

class FundScheduleService
{
    // =========================================================================
    // 일정 목록 조회
    // =========================================================================

    /**
     * 일정 목록 조회 (페이지네이션)
     */
    public function getSchedules(array $filters = [], int $perPage = 15): LengthAwarePaginator
    {
        $query = FundSchedule::query();

        // Soft Delete 필터
        if (isset($filters['trashed'])) {
            if ($filters['trashed'] === 'only') {
                $query->onlyTrashed();
            } elseif ($filters['trashed'] === 'with') {
                $query->withTrashed();
            }
        }

        // 검색 필터
        if (! empty($filters['search'])) {
            $search = $filters['search'];
            $query->where(function ($q) use ($search) {
                $q->where('title', 'like', "%{$search}%")
                    ->orWhere('counterparty', 'like', "%{$search}%")
                    ->orWhere('description', 'like', "%{$search}%");
            });
        }

        // 일정 유형 필터
        if (! empty($filters['schedule_type'])) {
            $query->where('schedule_type', $filters['schedule_type']);
        }

        // 상태 필터
        if (! empty($filters['status'])) {
            $query->where('status', $filters['status']);
        }

        // 날짜 범위 필터
        if (! empty($filters['start_date']) && ! empty($filters['end_date'])) {
            $query->dateBetween($filters['start_date'], $filters['end_date']);
        } elseif (! empty($filters['start_date'])) {
            $query->where('scheduled_date', '>=', $filters['start_date']);
        } elseif (! empty($filters['end_date'])) {
            $query->where('scheduled_date', '<=', $filters['end_date']);
        }

        // 월별 필터
        if (! empty($filters['year']) && ! empty($filters['month'])) {
            $query->forMonth((int) $filters['year'], (int) $filters['month']);
        }

        // 카테고리 필터
        if (! empty($filters['category'])) {
            $query->where('category', $filters['category']);
        }

        return $query
            ->with('bankAccount:id,bank_name,account_number')
            ->orderBy('scheduled_date')
            ->orderBy('id')
            ->paginate($perPage);
    }

    /**
     * 월별 일정 조회 (캘린더용)
     */
    public function getSchedulesForMonth(int $year, int $month): Collection
    {
        return FundSchedule::forMonth($year, $month)
            ->with('bankAccount:id,bank_name,account_number')
            ->ordered()
            ->get();
    }

    /**
     * 특정 날짜의 일정 조회
     */
    public function getSchedulesForDate(string $date): Collection
    {
        return FundSchedule::where('scheduled_date', $date)
            ->with('bankAccount:id,bank_name,account_number')
            ->ordered()
            ->get();
    }

    /**
     * 예정된 일정 조회 (향후 N일)
     */
    public function getUpcomingSchedules(int $days = 30): Collection
    {
        $startDate = now()->toDateString();
        $endDate = now()->addDays($days)->toDateString();

        return FundSchedule::pending()
            ->dateBetween($startDate, $endDate)
            ->with('bankAccount:id,bank_name,account_number')
            ->ordered()
            ->get();
    }

    // =========================================================================
    // 일정 CRUD
    // =========================================================================

    /**
     * 일정 상세 조회
     */
    public function getScheduleById(int $id, bool $withTrashed = false): ?FundSchedule
    {
        $query = FundSchedule::query();

        if ($withTrashed) {
            $query->withTrashed();
        }

        return $query->with('bankAccount')->find($id);
    }

    /**
     * 일정 생성
     */
    public function createSchedule(array $data): FundSchedule
    {
        $data['created_by'] = auth()->id();
        $data['tenant_id'] = $data['tenant_id'] ?? session('selected_tenant_id') ?? auth()->user()?->tenant_id;

        return FundSchedule::create($data);
    }

    /**
     * 일정 수정
     */
    public function updateSchedule(FundSchedule $schedule, array $data): FundSchedule
    {
        $data['updated_by'] = auth()->id();

        $schedule->update($data);

        return $schedule->fresh();
    }

    /**
     * 일정 삭제 (Soft Delete)
     */
    public function deleteSchedule(FundSchedule $schedule): bool
    {
        $schedule->deleted_by = auth()->id();
        $schedule->save();

        return $schedule->delete();
    }

    /**
     * 일정 복원
     */
    public function restoreSchedule(FundSchedule $schedule): bool
    {
        $schedule->deleted_by = null;

        return $schedule->restore();
    }

    /**
     * 일정 영구 삭제
     */
    public function forceDeleteSchedule(FundSchedule $schedule): bool
    {
        return $schedule->forceDelete();
    }

    // =========================================================================
    // 상태 변경
    // =========================================================================

    /**
     * 완료 처리
     */
    public function markAsCompleted(FundSchedule $schedule, ?float $actualAmount = null, ?string $completedDate = null): FundSchedule
    {
        $schedule->markAsCompleted($actualAmount, $completedDate);

        return $schedule->fresh();
    }

    /**
     * 취소 처리
     */
    public function markAsCancelled(FundSchedule $schedule): FundSchedule
    {
        $schedule->markAsCancelled();

        return $schedule->fresh();
    }

    /**
     * 상태 변경
     */
    public function updateStatus(FundSchedule $schedule, string $status): FundSchedule
    {
        $schedule->update([
            'status' => $status,
            'updated_by' => auth()->id(),
        ]);

        return $schedule->fresh();
    }

    // =========================================================================
    // 월별 복사
    // =========================================================================

    /**
     * 원본 월의 일정을 대상 월로 복사
     */
    public function copySchedulesToMonth(int $sourceYear, int $sourceMonth, int $targetYear, int $targetMonth): int
    {
        $schedules = $this->getSchedulesForMonth($sourceYear, $sourceMonth);

        if ($schedules->isEmpty()) {
            return 0;
        }

        $targetLastDay = cal_days_in_month(CAL_GREGORIAN, $targetMonth, $targetYear);
        $copiedCount = 0;

        foreach ($schedules as $schedule) {
            $sourceDay = $schedule->scheduled_date->day;
            $targetDay = min($sourceDay, $targetLastDay);

            $targetDate = sprintf('%04d-%02d-%02d', $targetYear, $targetMonth, $targetDay);

            $this->createSchedule([
                'title' => $schedule->title,
                'description' => $schedule->description,
                'schedule_type' => $schedule->schedule_type,
                'scheduled_date' => $targetDate,
                'amount' => $schedule->amount,
                'currency' => $schedule->currency,
                'related_bank_account_id' => $schedule->related_bank_account_id,
                'counterparty' => $schedule->counterparty,
                'category' => $schedule->category,
                'status' => FundSchedule::STATUS_PENDING,
                'is_recurring' => $schedule->is_recurring,
                'recurrence_rule' => $schedule->recurrence_rule,
                'recurrence_end_date' => $schedule->recurrence_end_date,
                'memo' => $schedule->memo,
            ]);

            $copiedCount++;
        }

        return $copiedCount;
    }

    // =========================================================================
    // 일괄 작업
    // =========================================================================

    /**
     * 일괄 삭제
     */
    public function bulkDelete(array $ids): int
    {
        return FundSchedule::whereIn('id', $ids)
            ->update([
                'deleted_by' => auth()->id(),
                'deleted_at' => now(),
            ]);
    }

    /**
     * 일괄 상태 변경
     */
    public function bulkUpdateStatus(array $ids, string $status): int
    {
        return FundSchedule::whereIn('id', $ids)
            ->update([
                'status' => $status,
                'updated_by' => auth()->id(),
            ]);
    }

    // =========================================================================
    // 요약 및 통계
    // =========================================================================

    /**
     * 월별 요약 통계
     */
    public function getMonthlySummary(int $year, int $month): array
    {
        $schedules = FundSchedule::forMonth($year, $month)->get();

        $incomeSchedules = $schedules->where('schedule_type', FundSchedule::TYPE_INCOME);
        $expenseSchedules = $schedules->where('schedule_type', FundSchedule::TYPE_EXPENSE);

        return [
            'year' => $year,
            'month' => $month,
            'total_count' => $schedules->count(),
            'income' => [
                'count' => $incomeSchedules->count(),
                'total' => $incomeSchedules->sum('amount'),
                'pending' => $incomeSchedules->where('status', FundSchedule::STATUS_PENDING)->sum('amount'),
                'completed' => $incomeSchedules->where('status', FundSchedule::STATUS_COMPLETED)->sum(fn ($s) => $s->completed_amount ?: $s->amount),
            ],
            'expense' => [
                'count' => $expenseSchedules->count(),
                'total' => $expenseSchedules->sum('amount'),
                'pending' => $expenseSchedules->where('status', FundSchedule::STATUS_PENDING)->sum('amount'),
                'completed' => $expenseSchedules->where('status', FundSchedule::STATUS_COMPLETED)->sum(fn ($s) => $s->completed_amount ?: $s->amount),
            ],
            'net' => $incomeSchedules->sum('amount') - $expenseSchedules->sum('amount'),
        ];
    }

    /**
     * 캘린더용 데이터 구조화
     */
    public function getCalendarData(int $year, int $month): array
    {
        $schedules = $this->getSchedulesForMonth($year, $month);

        // 날짜별로 그룹화
        $groupedByDate = $schedules->groupBy(function ($schedule) {
            return $schedule->scheduled_date->format('Y-m-d');
        });

        return $groupedByDate->toArray();
    }

    /**
     * 전체 요약 통계
     */
    public function getSummary(): array
    {
        $pending = FundSchedule::pending()->get();

        return [
            'pending_count' => $pending->count(),
            'pending_income' => $pending->where('schedule_type', FundSchedule::TYPE_INCOME)->sum('amount'),
            'pending_expense' => $pending->where('schedule_type', FundSchedule::TYPE_EXPENSE)->sum('amount'),
            'upcoming_7days' => FundSchedule::pending()
                ->dateBetween(now()->toDateString(), now()->addDays(7)->toDateString())
                ->count(),
        ];
    }
}
