<?php

declare(strict_types=1);

namespace App\Services\Project;

use App\Jobs\SyncSupplierProjectJob;
use App\Models\Project;
use App\Models\Tenant;
use Illuminate\Http\Exceptions\HttpResponseException;

class ProjectService
{
    public function update(Project $project, array $data): Project
    {
        // Immutable fields — silently drop (don't 422)
        unset(
            $data['tenant_id'], $data['signal_type'], $data['signal_identifier'],
            $data['delivered_today'], $data['delivered_in_month'],
            $data['supplier_b1_project_id'], $data['supplier_b2_project_id'], $data['supplier_b3_project_id'],
            $data['archived_at'],
        );

        if (isset($data['daily_limit_target']) && $data['daily_limit_target'] < $project->delivered_today) {
            throw new HttpResponseException(response()->json([
                'errors' => [
                    'daily_limit_target' => [
                        "Лимит не может быть меньше уже доставленных лидов сегодня ({$project->delivered_today}).",
                    ],
                ],
            ], 422));
        }

        $needsResync = array_key_exists('sms_senders', $data) || array_key_exists('sms_keyword', $data);

        $project->update($data);

        if ($needsResync) {
            SyncSupplierProjectJob::dispatch($project->id);
        }

        return $project->fresh();
    }

    public function archive(Project $project): void
    {
        if ($project->archived_at !== null) {
            throw new HttpResponseException(response()->json([
                'message' => 'Project уже архивирован.',
            ], 409));
        }
        $project->update([
            'is_active' => false,
            'archived_at' => now(),
        ]);
    }

    public function triggerSync(Project $project): void
    {
        SyncSupplierProjectJob::dispatch($project->id);
    }

    public const BULK_MAX = 500;

    public function resolveBulkScope(int $tenantId, ?array $ids, ?array $filter): array
    {
        if (! empty($ids)) {
            return array_values(array_unique($ids));
        }

        $query = Project::where('tenant_id', $tenantId);

        if (! empty($filter['signal_type'])) {
            $query->where('signal_type', $filter['signal_type']);
        }
        if (! empty($filter['status'])) {
            match ($filter['status']) {
                'active' => $query->where('is_active', true)->whereNull('archived_at'),
                'paused' => $query->where('is_active', false)->whereNull('archived_at'),
                'archived' => $query->whereNotNull('archived_at'),
                default => null,
            };
        }
        if (! empty($filter['search'])) {
            $query->where('name', 'ilike', '%'.$filter['search'].'%');
        }

        return $query->pluck('id')->all();
    }

    public function bulkAction(int $tenantId, string $action, array $payload): array
    {
        $ids = $payload['ids'] ?? [];
        if (empty($ids)) {
            return ['updated' => 0, 'skipped' => [], 'warnings' => []];
        }

        $query = Project::where('tenant_id', $tenantId)->whereIn('id', $ids);

        return match ($action) {
            'pause' => $this->bulkSimpleUpdate($query, ['is_active' => false]),
            'resume' => $this->bulkSimpleUpdate($query, ['is_active' => true]),
            'archive' => $this->bulkSimpleUpdate($query, ['is_active' => false, 'archived_at' => now()]),
            'update_regions' => $this->bulkUpdateRegions($query, $payload),
            'update_days' => $this->bulkUpdateDays($query, $payload),
            'update_limit' => $this->bulkUpdateLimit($query, $payload),
        };
    }

    private function bulkSimpleUpdate($query, array $update): array
    {
        $updated = $query->update($update);

        return ['updated' => $updated, 'skipped' => [], 'warnings' => []];
    }

    private function bulkUpdateRegions($query, array $payload): array
    {
        $add = (int) ($payload['add'] ?? 0);
        $remove = (int) ($payload['remove'] ?? 0);

        // region_mask = (region_mask | add) & ~remove, clamped to 8 bits (0–255)
        $updated = $query->update([
            'region_mask' => \DB::raw("(region_mask | {$add}) & ~{$remove} & 255"),
        ]);

        return ['updated' => $updated, 'skipped' => [], 'warnings' => []];
    }

    private function bulkUpdateDays($query, array $payload): array
    {
        $add = (int) ($payload['add'] ?? 0);
        $remove = (int) ($payload['remove'] ?? 0);

        $updated = $query->update([
            'delivery_days_mask' => \DB::raw("(delivery_days_mask | {$add}) & ~{$remove} & 127"),
        ]);

        return ['updated' => $updated, 'skipped' => [], 'warnings' => []];
    }

    private function bulkUpdateLimit($query, array $payload): array
    {
        $delta = $payload['delta'] ?? null;
        $replace = $payload['replace'] ?? null;

        $projects = (clone $query)->select(['id', 'daily_limit_target', 'delivered_today'])->get();

        $updatableIds = [];
        $skipped = [];

        foreach ($projects as $p) {
            $newValue = $replace !== null
                ? (int) $replace
                : (int) $p->daily_limit_target + (int) $delta;

            if ($newValue < (int) $p->delivered_today) {
                $skipped[] = ['id' => $p->id, 'reason' => 'below_delivered_today'];
            } else {
                $updatableIds[$p->id] = $newValue;
            }
        }

        $updated = 0;

        if (! empty($updatableIds)) {
            if ($replace !== null) {
                $updated = Project::whereIn('id', array_keys($updatableIds))
                    ->update(['daily_limit_target' => (int) $replace]);
            } else {
                // delta — обновляем по одному (count bounded by MAX 500).
                foreach ($updatableIds as $id => $newValue) {
                    Project::where('id', $id)->update(['daily_limit_target' => $newValue]);
                    $updated++;
                }
            }
        }

        return ['updated' => $updated, 'skipped' => $skipped, 'warnings' => []];
    }

    public function create(Tenant $tenant, array $data): Project
    {
        $limit = (int) ($tenant->limits['max_projects'] ?? 10);
        $current = Project::where('tenant_id', $tenant->id)->active()->count();
        if ($current >= $limit) {
            throw new HttpResponseException(response()->json([
                'message' => "Достигнут лимит проектов ({$limit}). Смените тариф.",
            ], 403));
        }

        $data['tenant_id'] = $tenant->id;
        $data['is_active'] = true;
        $project = Project::create($data);

        SyncSupplierProjectJob::dispatch($project->id);

        return $project->fresh();
    }
}
