<?php

namespace App\Master\Http\Controllers;

use App\Http\Controllers\Controller;
use App\Http\Resources\ContentSyncResource;
use App\Mail\ContentSyncApprovalActionedMail;
use App\Mail\ContentSyncApprovalRequestedMail;
use App\Master\ContentSyncRepository;
use App\Master\Http\Requests\ContentSyncDataRequest;
use App\Master\Http\Requests\ContentSyncRequest;
use App\Master\Http\Requests\CreateContentSyncRequest;
use App\Master\Http\Requests\UpdateContentSyncRequest;
use App\Models\ContentSync;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Mail;
use Mtc\MercuryDataModels\Tenant;
use Mtc\MercuryDataModels\User;

class ContentSyncController extends Controller
{
    public function index(Request $request)
    {
        return ContentSync::query()
            ->with([
                'user',
                'tenant',
                'approvedByUser',
            ])
            ->when(
                $request->input('approval_status'),
                fn ($query, $status) => $query->where('approval_status', $status)
            )
            ->latest()
            ->paginate();
    }

    public function show(ContentSync $contentSync)
    {
        return new ContentSyncResource($contentSync);
    }

    public function store(
        CreateContentSyncRequest $request,
        ContentSyncRepository $repository
    ) {
        $tenant = Tenant::find($request->input('tenant_id'));
        $isLiveSite = !empty($tenant?->live_at);
        $writesToLiveSite = $isLiveSite
            && in_array($request->input('direction'), [ContentSync::RECEIVE, ContentSync::PULL]);

        if (in_array($request->input('direction'), [ContentSync::PROVIDE, ContentSync::RECEIVE])) {
            $token = $repository->provideToken($request->input());

            if ($writesToLiveSite) {
                $token->update([
                    'approval_status' => ContentSync::APPROVAL_PENDING,
                    'approval_reason' => $request->input('reason'),
                ]);
                $this->notifyApprovers($token);
            }

            return response([
                'status' => $writesToLiveSite ? 'pending_approval' : 'ok',
                'id' => $token->id,
            ]);
        }

        if ($writesToLiveSite) {
            $sync = $repository->provideToken(array_merge($request->input(), [
                'direction' => $request->input('direction'),
            ]));
            $sync->update([
                'approval_status' => ContentSync::APPROVAL_PENDING,
                'approval_reason' => $request->input('reason'),
                'details' => $request->input(),
            ]);
            $this->notifyApprovers($sync);

            return response([
                'status' => 'pending_approval',
                'id' => $sync->id,
            ]);
        }

        return response($repository->attemptSync($request->input()));
    }

    public function approve(Request $request, ContentSync $sync): JsonResponse
    {
        $user = $request->user();

        if (!$user->hasPermissionTo('approve-content-sync')) {
            return response()->json([
                'message' => 'You do not have permission to approve content sync requests.',
            ], 422);
        }

        if (!$sync->isPendingApproval()) {
            return response()->json([
                'message' => 'This sync request has already been processed.',
            ], 422);
        }

        if ($sync->user_id === $user->id) {
            return response()->json([
                'message' => 'You cannot approve your own content sync request.',
            ], 422);
        }

        $validated = $request->validate([
            'approval_notes' => 'nullable|string|max:1000',
        ]);

        $sync->update([
            'approval_status' => ContentSync::APPROVAL_APPROVED,
            'approved_by' => $user->id,
            'approved_at' => now(),
            'approval_notes' => $validated['approval_notes'] ?? null,
            'created_at' => now(),
        ]);

        $sync->load(['user', 'tenant', 'approvedByUser']);

        Mail::to($sync->user->email)
            ->send(new ContentSyncApprovalActionedMail($sync));

        return response()->json([
            'message' => 'Content sync request approved.',
            'sync' => new ContentSyncResource($sync),
        ]);
    }

    public function reject(Request $request, ContentSync $sync): JsonResponse
    {
        $user = $request->user();

        if (!$user->hasPermissionTo('approve-content-sync')) {
            return response()->json([
                'message' => 'You do not have permission to reject content sync requests.',
            ], 422);
        }

        if (!$sync->isPendingApproval()) {
            return response()->json([
                'message' => 'This sync request has already been processed.',
            ], 422);
        }

        $validated = $request->validate([
            'approval_notes' => 'required|string|max:1000',
        ]);

        $sync->update([
            'approval_status' => ContentSync::APPROVAL_REJECTED,
            'approved_by' => $user->id,
            'approved_at' => now(),
            'approval_notes' => $validated['approval_notes'],
        ]);

        $sync->load(['user', 'tenant', 'approvedByUser']);

        Mail::to($sync->user->email)
            ->send(new ContentSyncApprovalActionedMail($sync));

        return response()->json([
            'message' => 'Content sync request rejected.',
        ]);
    }

    public function getRemoteEntries(Request $request, ContentSyncRepository $repository)
    {
        return $repository->pullDataFromRemote($request->input());
    }

    public function provideEntries(
        ContentSyncDataRequest $request,
        ContentSyncRepository $repository
    ) {
        tenancy()->initialize($request->sync()->tenant_id);
        $data = $repository->provideData($request->input());
        tenancy()->end();
        return response($data);
    }

    public function update(Request $request, ContentSync $sync)
    {
        $sync->update([
            'data' => collect($request->input('data'))
                ->pluck('data')
                ->toArray(),
        ]);
        return new ContentSyncResource($sync);
    }

    public function receiveDataFromRemote(ContentSyncRequest $request)
    {
        $details = $request->sync()->details ?? [];
        $details['scope'] = $request->input('scope');
        $request->sync()
            ->update([
                'details' => $details,
                'data' => $request->input('data'),
            ]);
        return response('Data received');
    }

    public function run(Request $request, ContentSyncRepository $repository)
    {
        $details = $request->input('details', []);
        $details['tenant_id'] = $request->input('tenant_id');
        return $repository->importData($details, $request->input('data'));
    }

    public function renewToken(
        UpdateContentSyncRequest $request,
        ContentSyncRepository $repository,
        ContentSync $sync
    ): ContentSyncResource {
        if ($sync->isPendingApproval() || $sync->isRejected()) {
            abort(422, 'Cannot renew token for a sync that is pending approval or rejected.');
        }

        $sync = $repository->renewToken($sync);
        return new ContentSyncResource($sync);
    }

    private function notifyApprovers(ContentSync $sync): void
    {
        $sync->load(['user', 'tenant']);
        $approvers = User::permission('approve-content-sync')->get();

        foreach ($approvers as $approver) {
            Mail::to($approver->email)
                ->send(new ContentSyncApprovalRequestedMail($sync));
        }
    }
}
