<?php

namespace App\Master\Http\Controllers;

use App\Facades\Feature;
use App\Http\Controllers\Controller;
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\Validation\ValidationException;
use Mtc\MercuryDataModels\Billable;
use Mtc\MercuryDataModels\Role;
use Mtc\MercuryDataModels\Tenant;

class SiteController extends Controller
{
    public function __construct(private 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'))
            )
            ->notMtc()
            ->get();
    }

    public function queueLengths(Factory $manager)
    {
        return collect(config('app.active-queues'))->map(fn($queueName) => [
            'name' => __('labels.queues.' . $queueName),
            'value' => $manager->connection(config('queue.default'))->size($queueName),
        ]);
    }

    /**
     * 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');

        $sites = Tenant::query()
            ->orderBy('name')
            ->with([
                'users',
                'domains',
                'features',
            ])
            ->when(
                !empty($request->input('term')),
                fn($query) => $query->where('name', 'like', '%' . trim($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);
    }

    /**
     * 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
    {
        $site->load([
            'billables',
            'features',
        ]);

        $resource = $site->toArray();
        $resource['cloudflare_data'] = [
            'zone_id' => $site->cloudflare_data['zone_id'] ?? null,
            'email' => $site->cloudflare_data['email'] ?? null,
            'key' => $site->cloudflare_data['key'] ?? null,
        ];
        $resource['enabled_features'] = $site->features->pluck('feature');

        return [
            'site' => $resource,
            'billables' => Billable::all()->groupBy('type'),
            'features' => Feature::getAll(),
        ];
    }

    public function updateBilling(Request $request, Tenant $site): Response
    {
        $site->matomo_site_id = $request->input('matomo_site_id');
        $site->update([
            '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,
        ]);

        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(
            Tenant::query()
                ->whereNull('suspended_at')
                ->get()
                ->map(fn(Tenant $tenant) => $repository->enabledByType($tenant)->merge([
                    'name' => $tenant->name,
                    'id' => $tenant->id,
                    'tier' => $tenant->tier,
                ]))
        );
    }
}
