<?php

namespace App\Development\Oculi;

use Illuminate\Http\Client\Request;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;

/**
 * Oculi mock (App\Repositories\OculiRepository, App\Console\Commands\Oculi). Oculi itself is a separate
 * service; the read endpoints are served from JSON scenario files under Development/Oculi, while the
 * write endpoints just accept the payload and echo back enough for the commands to settle their queues.
 *
 * Carpets ride the same scenario: Carpets.json names each carpet's organisation and member attacks, and a
 * carpet's attack table is the organisation's attack table cut down to those members.
 */
class Routes
{
    // POST /mitigation/events
    public function events(array $params, Request $request)
    {
        $events = $params['events'] ?? [];

        Log::info('Oculi mock accepted ' . sizeof($events) . ' mitigation event entries.', [
            'entries' => array_slice($events, 0, 25)
        ]);

        return Http::response(json_encode([ 'accepted' => sizeof($events) ]), 200, [ 'Content-Type' => 'application/json' ]);
    }

    // PUT /mitigation/organisations
    public function organisations(array $params, Request $request)
    {
        $organisations = $params['organisations'] ?? [];

        Log::info('Oculi mock accepted ' . sizeof($organisations) . ' organisation entries.');

        return Http::response(json_encode([ 'stored' => sizeof($organisations) ]), 200, [ 'Content-Type' => 'application/json' ]);
    }

    // PUT /mitigation/prefixes - the complete (parent prefix, organisation) picture, every time.
    public function prefixes(array $params, Request $request)
    {
        $prefixes = $params['prefixes'] ?? [];

        Log::info('Oculi mock accepted ' . sizeof($prefixes) . ' prefix visibility entries.');

        return Http::response(json_encode([ 'stored' => sizeof($prefixes), 'added' => sizeof($prefixes), 'removed' => 0 ]), 200, [ 'Content-Type' => 'application/json' ]);
    }

    // GET /mitigation/attacks/table
    public function attacks_table(array $params, Request $request)
    {
        $data = $this->table('AttackTable', $request->data()['organisation_id'] ?? 'Admin');
        $data['draw'] = (int) $request['draw'];

        return Http::response(json_encode($data), 200);
    }

    // GET /mitigation/attacks/summary
    public function attacks_summary(array $params, Request $request)
    {
        $scenarios = json_decode(file_get_contents(app_path('Development/Oculi/Summaries.json')), true);
        $organisation = (string) ($request->data()['organisation_id'] ?? 'admin');

        return Http::response(json_encode($scenarios[$organisation] ?? [ 'ongoing' => 0, 'past_day' => 0, 'past_week' => 0, 'carpets' => [ 'ongoing' => 0, 'past_day' => 0, 'past_week' => 0 ] ]), 200);
    }

    // GET /mitigation/attacks/ongoing
    public function attacks_ongoing(array $params, Request $request)
    {
        $scenarios = json_decode(file_get_contents(app_path('Development/Oculi/Ongoing.json')), true);
        $organisation = (string) ($request->data()['organisation_id'] ?? 'admin');

        return Http::response(json_encode($scenarios[$organisation] ?? [ 'data' => [] ]), 200);
    }

    // GET /mitigation/attacks/{id}/events - the mitigation events behind one attack.
    public function attack_events(array $params, Request $request)
    {
        if(!preg_match('#/mitigation/attacks/([0-9a-f-]+)/events#', $request->url(), $matched)) {
            return Http::response(json_encode([ 'error' => 'Attack not found' ]), 404);
        }

        $scenarios = json_decode(file_get_contents(app_path('Development/Oculi/Events.json')), true);

        /**
         * Scoped exactly like the live service - the wrong organisation and a missing attack are
         * indistinguishable, both are a 404.
         */
        $events = $scenarios[$matched[1]] ?? null;

        if($events === null || (isset($request->data()['organisation_id']) && (int) $request->data()['organisation_id'] !== $events['organisation_id'])) {
            return Http::response(json_encode([ 'error' => 'Attack not found' ]), 404);
        }

        return Http::response(json_encode([ 'attack_id' => $matched[1], 'data' => $events['data'] ]), 200);
    }

    // GET /mitigation/attacks/{id}/report - JSON report, or a stand-in PDF body.
    public function attack_report(array $params, Request $request)
    {
        if(!preg_match('#/mitigation/attacks/([0-9a-f-]+)/report#', $request->url(), $matched)) {
            return Http::response(json_encode([ 'error' => 'Attack not found' ]), 404);
        }

        $reports = json_decode(file_get_contents(app_path('Development/Oculi/Reports.json')), true);

        /**
         * Reports are scoped exactly like the live service - a wrong organisation and a missing attack
         * are indistinguishable, both are a 404.
         */
        $report = $reports[$matched[1]] ?? null;

        if($report === null || (isset($request->data()['organisation_id']) && (int) $request->data()['organisation_id'] !== $report['attack']['organisation_id'])) {
            return Http::response(json_encode([ 'error' => 'Attack not found' ]), 404);
        }

        if(($request->data()['format'] ?? null) === 'pdf') {
            return Http::response("%PDF-1.4\n% Oculi development mock report for {$matched[1]}\n", 200, [ 'Content-Type' => 'application/pdf' ]);
        }

        return Http::response(json_encode($report), 200);
    }

    // GET /mitigation/carpets/table
    public function carpets_table(array $params, Request $request)
    {
        $data = $this->table('CarpetTable', $request->data()['organisation_id'] ?? 'Admin');
        $data['draw'] = (int) $request['draw'];

        return Http::response(json_encode($data), 200);
    }

    // GET /mitigation/carpets/summary
    public function carpets_summary(array $params, Request $request)
    {
        $scenarios = json_decode(file_get_contents(app_path('Development/Oculi/CarpetSummaries.json')), true);
        $organisation = (string) ($request->data()['organisation_id'] ?? 'admin');

        return Http::response(json_encode($scenarios[$organisation] ?? [ 'ongoing' => 0, 'past_day' => 0, 'past_week' => 0 ]), 200);
    }

    // GET /mitigation/carpets/ongoing
    public function carpets_ongoing(array $params, Request $request)
    {
        $scenarios = json_decode(file_get_contents(app_path('Development/Oculi/CarpetOngoing.json')), true);
        $organisation = (string) ($request->data()['organisation_id'] ?? 'admin');

        return Http::response(json_encode($scenarios[$organisation] ?? [ 'data' => [] ]), 200);
    }

    // GET /mitigation/carpets/{id}/attacks - the organisation's attack table cut down to the carpet's members.
    public function carpet_attacks(array $params, Request $request)
    {
        if(!preg_match('#/mitigation/carpets/([0-9a-f-]+)/attacks#', $request->url(), $matched)) {
            return Http::response(json_encode([ 'error' => 'Not found' ]), 404);
        }

        $carpets = json_decode(file_get_contents(app_path('Development/Oculi/Carpets.json')), true);

        // Scoped exactly like the live service - the wrong organisation and a missing carpet are the same 404.
        $carpet = $carpets[$matched[1]] ?? null;

        if($carpet === null || (isset($request->data()['organisation_id']) && (int) $request->data()['organisation_id'] !== $carpet['organisation_id'])) {
            return Http::response(json_encode([ 'error' => 'Not found' ]), 404);
        }

        $table = $this->table('AttackTable', $carpet['organisation_id']);

        $members = array_values(array_filter($table['data'], fn ($row) => in_array($row['attack_id'], $carpet['members'], true)));

        return Http::response(json_encode([
            'draw'              => (int) $request['draw'],
            'recordsTotal'      => sizeof($members),
            'recordsFiltered'   => sizeof($members),
            'data'              => $members
        ]), 200);
    }

    // GET /mitigation/carpets/{id}/report - JSON only; the live service has no carpet PDF template yet and says so.
    public function carpet_report(array $params, Request $request)
    {
        if(!preg_match('#/mitigation/carpets/([0-9a-f-]+)/report#', $request->url(), $matched)) {
            return Http::response(json_encode([ 'error' => 'Not found' ]), 404);
        }

        $reports = json_decode(file_get_contents(app_path('Development/Oculi/CarpetReports.json')), true);

        $report = $reports[$matched[1]] ?? null;

        if($report === null || (isset($request->data()['organisation_id']) && (int) $request->data()['organisation_id'] !== $report['carpet']['organisation_id'])) {
            return Http::response(json_encode([ 'error' => 'Not found' ]), 404);
        }

        if(($request->data()['format'] ?? null) === 'pdf') {
            return Http::response(json_encode([ 'error' => 'Not implemented' ]), 501);
        }

        return Http::response(json_encode($report), 200);
    }

    // GET /mitigation/leaderboard
    public function leaderboard(array $params, Request $request)
    {
        $data = json_decode(file_get_contents(app_path('Development/Oculi/Leaderboard.json')), true);
        $data['draw'] = (int) $request['draw'];

        return Http::response(json_encode($data), 200);
    }

    /**
     * A table scenario for an organisation: #1 and #2 have their own files, no organisation is the admin
     * view, and anyone else is empty.
     */
    private function table(string $kind, $organisation): array
    {
        if(array_search($organisation, [ 1, 2, 'Admin' ]) === false) {
            $organisation = 'Empty';
        }

        return json_decode(file_get_contents(app_path("Development/Oculi/Tables/$kind-$organisation.json")), true);
    }
}
