<?php

namespace App\Master\Http\Controllers;

use App\Contracts\GoLiveChecklist;
use App\Contracts\GoLiveStatus;
use App\AdminDashboardRepository;
use App\Facades\Feature;
use App\Http\Controllers\Controller;
use App\Http\Resources\SiteGoLiveResource;
use App\IntegrationRepository;
use App\Jobs\RefreshFilterIndexJob;
use App\Master\Http\Requests\InviteUserToSiteRequest;
use App\Master\Http\Requests\RemoveUserFromSiteRequest;
use App\Master\Http\Requests\SiteRequest;
use App\Master\Http\Requests\SiteStoreRequest;
use App\Master\Http\Requests\UpdateUserRoleOnSiteRequest;
use App\Master\Http\Resources\SiteListResource;
use App\Master\SiteManager;
use App\Services\CloudflareService;
use App\Tier;
use Exception;
use Illuminate\Contracts\Queue\Factory;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\ValidationException;
use Mtc\MercuryDataModels\Billable;
use Mtc\MercuryDataModels\Role;
use Mtc\MercuryDataModels\Tenant;
use Mtc\MercuryDataModels\TenantType;

class SiteController extends Controller
{
    public function __construct(private readonly SiteManager $manager)
    {
        $this->middleware([
            'permission:manage-sites'
        ]);
    }

    /**
     * List available user roles on site
     *
     * @param SiteRequest $request
     * @return mixed
     */
    public function roles(SiteRequest $request)
    {
        return Role::query()
            ->when(
                $request->filled('tenant'),
                fn($query) => $query->where(fn($query) => $query->where('tenant', $request->input('tenant'))
                    ->orWhereNull('tenant'))
            )
            ->when(
                !$request->boolean('include_mtc'),
                fn($query) => $query->notMtc()
            )
            ->get();
    }

    public function queueLengths(Factory $manager)
    {
        $queueIcons = [
            'sync' => 'arrows-rotate',
            'specs' => 'file-magnifying-glass',
            'bulk-media' => 'images',
            'media' => 'image',
            'export' => 'file-export',
            'default' => 'layer-group',
            'leasing-sync' => 'car-side',
            'motors-batch' => 'cars',
        ];

        return collect(config('app.active-queues'))->map(fn($queueName) => [
            'key' => $queueName,
            'name' => __('labels.queues.' . $queueName),
            'value' => $manager->connection(config('queue.default'))->size($queueName),
            'icon' => $queueIcons[$queueName] ?? 'layer-group',
        ]);
    }

    public function failedJobs(Request $request)
    {
        $limit = $request->input('limit', 20);
        $queue = $request->input('queue');

        $jobs = DB::table('failed_jobs')
            ->when($queue, fn($q) => $q->where('queue', $queue))
            ->orderBy('failed_at', 'desc')
            ->limit($limit)
            ->get()
            ->map(function ($job) {
                $payload = json_decode($job->payload, true);
                $displayName = $payload['displayName'] ?? 'Unknown Job';

                // Extract short class name
                $shortName = class_basename($displayName);

                // Extract first line of exception for summary
                $exceptionLines = explode("\n", $job->exception);
                $exceptionSummary = $exceptionLines[0] ?? 'No exception details';

                // Extract meaningful payload data (exclude serialized command)
                $payloadData = collect($payload)
                    ->except(['command'])
                    ->merge([
                        'job' => $displayName,
                        'connection' => $job->connection,
                    ])
                    ->toArray();

                return [
                    'uuid' => $job->uuid,
                    'queue' => $job->queue,
                    'job_name' => $shortName,
                    'job_class' => $displayName,
                    'exception_summary' => $exceptionSummary,
                    'exception' => $job->exception,
                    'payload' => $payloadData,
                    'failed_at' => $job->failed_at,
                ];
            });

        $totalCount = DB::table('failed_jobs')->count();

        return [
            'jobs' => $jobs,
            'total' => $totalCount,
        ];
    }

    /**
     * Display a list of sites in system.
     *
     * @return SiteListResource
     */
    public function index(SiteRequest $request): SiteListResource
    {
        $exclude_suspended = filter_var($request->input('exclude_suspended'), FILTER_VALIDATE_BOOLEAN);
        $filter = $request->input('filters');
        $favouriteSiteIds = $request->user()->favouriteSites()->pluck('tenant_id')->toArray();

        $sites = Tenant::query()
            ->when(
                !empty($favouriteSiteIds),
                function ($query) use ($favouriteSiteIds) {
                    $favourites = implode(',', array_map(fn($id) => "'{$id}'", $favouriteSiteIds));
                    return $query
                        ->selectRaw('*, CASE WHEN id IN (' . $favourites . ') THEN 0 ELSE 1 END as is_favourite_sort')
                        ->orderBy('is_favourite_sort');
                }
            )
            ->orderBy('name')
            ->with([
                'users',
                'domains',
                'features',
            ])
            ->when(
                !empty($request->input('term')),
                fn($query) => $query->where('name', 'like', '%' . trim((string) $request->input('term')) . '%')
            )
            ->when($filter === 'live', fn($query) => $query->whereNotNull('live_at'))
            ->when($filter === 'in_development', fn($query) => $query->whereNull('suspended_at')->whereNull('live_at'))
            ->when($filter === 'suspended', fn($query) => $query->whereNotNull('suspended_at'))
            ->when($exclude_suspended, fn($query) => $query->whereNull('suspended_at'))
            ->paginate($request->input('per_page', 15));

        return new SiteListResource($sites, $favouriteSiteIds);
    }

    /**
     * Store a newly created resource in storage.
     *
     * @param Request $request
     * @return SiteListResource
     */
    public function store(SiteStoreRequest $request): SiteListResource
    {
        $this->manager->addSiteRecord($request->input('name'), '', $request->input('tier'));
        return $this->index($request);
    }

    /**
     * Invite user to site
     *
     * @param InviteUserToSiteRequest $request
     * @param Tenant $site
     * @return SiteListResource|string[]
     */
    public function invite(InviteUserToSiteRequest $request, Tenant $site)
    {
        if ($this->manager->addUser($request->input('email'), $site->id, $request->input('role'))) {
            return $this->index($request);
        }
        return [
            'error' => 'Failed to add user',
        ];
    }

    /**
     * Remove user from site
     *
     * @param RemoveUserFromSiteRequest $request
     * @param Tenant $site
     * @return SiteListResource|string[]
     */
    public function removeUser(RemoveUserFromSiteRequest $request, Tenant $site): array|SiteListResource
    {
        if ($this->manager->removeUser($request->input('userId'), $site->id)) {
            return $this->index($request);
        }

        return [
            'error' => 'Failed to remove user from site',
        ];
    }

    /**
     * Update users role on site
     *
     * @param UpdateUserRoleOnSiteRequest $request
     * @param Tenant $site
     * @return SiteListResource|string[]
     */
    public function updateUserRole(UpdateUserRoleOnSiteRequest $request, Tenant $site): array|SiteListResource
    {
        if ($this->manager->changeUserRole($site->id, $request->input('userId'), $request->input('role'))) {
            return $this->index($request);
        }
        return [
            'error' => 'Failed to update user',
        ];
    }

    public function destroy(Tenant $site)
    {
        if (empty($site->suspended_at)) {
            throw ValidationException::withMessages([
                'Site needs to be suspended before deletion',
            ]);
        }

        $site->delete();
        return \response('ok');
    }

    /**
     * Suspend/Deactivate  site
     *
     * @param SiteRequest $request
     * @param Tenant $site
     * @return SiteListResource|string[]
     */
    public function suspendSite(SiteRequest $request, Tenant $site): array|SiteListResource
    {
        if ($this->manager->suspendSite($site, $request->user(), $request->input('message', ''))) {
            return $this->index($request);
        }
        return [
            'error' => 'Failed to suspend site',
        ];
    }

    public function markLive(SiteRequest $request, Tenant $site): Response
    {
        if ($this->manager->markLive($site, $request->user())) {
            return \response('ok');
        }
        return response([
            'error' => 'Failed to mark site live',
        ]);
    }

    /**
     * Unsuspend / reactivate site
     *
     * @param SiteRequest $request
     * @param Tenant $site
     * @return SiteListResource|string[]
     */
    public function unsuspendSite(SiteRequest $request, Tenant $site): array|SiteListResource
    {
        if ($this->manager->unsuspendSite($site)) {
            return $this->index($request);
        }
        return [
            'error' => 'Failed to unsuspend site',
        ];
    }

    public function billing(Tenant $site): array
    {
        tenancy()->initialize($site);

        return [
            'site' => new SiteGoLiveResource($site),
            'billables' => Billable::all()->groupBy('type'),
            'features' => Feature::getAll(),
            'types' => array_map(fn($type) => [
                'value' => $type->value,
                'label' => ucfirst((string) $type->value),
            ], TenantType::cases()),
        ];
    }

    public function goLiveTasks(): SiteGoLiveResource
    {
        return new SiteGoLiveResource(tenant());
    }

    public function triggerGoLiveTask(string $key): SiteGoLiveResource
    {
        collect(config('app.go_live_checklist', []))
            ->map(fn($checklistClass) => App::make($checklistClass))
            ->filter(fn(GoLiveChecklist $task) => $task->key() === $key)
            ->each(fn(GoLiveChecklist $task) => $task->start(true));
        return new SiteGoLiveResource(tenant());
    }

    public function triggerAllGoLive(): SiteGoLiveResource
    {
        collect(config('app.go_live_checklist', []))
            ->map(fn($checklistClass) => App::make($checklistClass))
            ->reject(fn(GoLiveChecklist $task) => $task->status() === GoLiveStatus::COMPLETED)
            ->reject(fn(GoLiveChecklist $task) => $task->requiresManualConfirm())
            ->each(fn(GoLiveChecklist $task) => $task->start());
        return new SiteGoLiveResource(tenant());
    }

    public function resetGoLive(): SiteGoLiveResource
    {
        tenant()->goLiveTasks()->delete();
        return new SiteGoLiveResource(tenant());
    }

    public function updateBilling(Request $request, Tenant $site): Response
    {
        $site->matomo_site_id = $request->input('matomo_site_id');
        $site->update([
            'type' => $request->input('type'),
            'name' => $request->input('name'),
            'tier' => $request->input('tier'),
            'has_cloudflare' => $request->input('has_cloudflare'),
            'cloudflare_data' => $request->input('cloudflare_data'),
            'theme_name' => $request->input('tier') === Tier::ENTERPRISE->value ? $request->input('theme_name') : null,
            'description' => $request->input('description'),
            'proposed_launch_date' => $request->input('proposed_launch_date'),
            'launch_confidence' => $request->input('launch_confidence'),
            'license_fee_amount' => $request->input('license_fee_amount'),
            'has_retainer' => $request->boolean('has_retainer'),
            'retainer_hours' => $request->input('retainer_hours'),
            'retainer_amount' => $request->input('retainer_amount'),
            'retainer_from' => $request->input('retainer_from'),
            'retainer_to' => $request->input('retainer_to'),
            'other_monthly_income' => $request->input('other_monthly_income'),
            'pre_launch_monthly_enquiries' => $request->input('pre_launch_monthly_enquiries'),
            'pre_launch_daily_enquiries' => $request->input('pre_launch_daily_enquiries'),
            'pre_launch_daily_visitors' => $request->input('pre_launch_daily_visitors'),
        ]);

        Feature::setEnabled($site, $request->input('enabled_features', []));
        return response('success');
    }

    public function clearCloudflare(Tenant $site, CloudflareService $service)
    {
        if (!$site->has_cloudflare) {
            throw ValidationException::withMessages(['Site does not have cloudflare enabled']);
        }

        $response = $service->clear($site);
        if ($response->successful() && $response->json('success')) {
            return response('Cache cleared');
        }
        throw ValidationException::withMessages(collect($response->json('errors'))->pluck('message')->toArray());
    }

    /**
     * @throws Exception
     */
    public function triggerAction(Request $request, Tenant $site): Response
    {
        $this->validate($request, ['action' => 'required']);
        tenancy()->initialize($site->id);
        match ($request->input('action')) {
            'refresh-filter' => $this->dispatch(new RefreshFilterIndexJob()),
            default => throw new \Exception('Unrecognized action'),
        };
        return response('success');
    }

    public function integrations(IntegrationRepository $repository): Response
    {
        return response([
            'integrations' => $repository->metadata(),
            'tenants' => Tenant::query()
                ->whereNull('suspended_at')
                ->get()
                ->map(fn(Tenant $tenant) => $repository->enabledByType($tenant)->merge([
                    'name' => $tenant->name,
                    'id' => $tenant->id,
                    'tier' => $tenant->tier,
                ])),
        ]);
    }
}
