<?php

declare(strict_types=1);

namespace App\Http\Controllers\Api\V1\Me;

use App\Http\Controllers\Controller;
use App\Models\AuditLog;
use App\Models\ConsentRecord;
use App\Services\Compliance\AuditLogger;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Str;
use Symfony\Component\HttpFoundation\StreamedResponse;
use ZipArchive;

/**
 * Personal Information Protection Act (PIPA Art. 21) +
 * GDPR Art. 15-22 self-service endpoints.
 *
 * @OA\Tag(name="Privacy", description="Data subject rights — self-service")
 */
final class PrivacyController extends Controller
{
    public function __construct(private readonly AuditLogger $audit)
    {
    }

    /**
     * @OA\Get(
     *   path="/api/v1/me/data-export",
     *   tags={"Privacy"},
     *   summary="Download own personal data as a ZIP (right to portability)",
     *   security={{"sanctum":{}}},
     *   @OA\Response(response=200, description="application/zip")
     * )
     */
    public function dataExport(Request $request): StreamedResponse|JsonResponse
    {
        $user = $request->user();
        if (! $user) {
            return response()->json(['error' => 'unauthenticated'], 401);
        }

        $this->audit->log('privacy.data_export.requested', $user);

        $bundle = [
            'profile' => $user->only(['id', 'email', 'username', 'name', 'created_at', 'updated_at']),
            'consents' => ConsentRecord::query()->where('user_id', $user->id)->get()->toArray(),
            'audit_logs' => AuditLog::query()
                ->where('user_id', $user->id)
                ->orderBy('created_at')
                ->limit(5000)
                ->get(['action', 'model_type', 'model_id', 'ip', 'created_at'])
                ->toArray(),
            'posts' => Schema::hasTable('posts')
                ? DB::table('posts')->where('user_id', $user->id)->get()->toArray()
                : [],
            'comments' => Schema::hasTable('post_comments')
                ? DB::table('post_comments')->where('user_id', $user->id)->get()->toArray()
                : [],
            'reactions' => Schema::hasTable('post_reactions')
                ? DB::table('post_reactions')->where('user_id', $user->id)->get()->toArray()
                : [],
            'generated_at' => now()->toIso8601String(),
        ];

        $tmpFile = tempnam(sys_get_temp_dir(), 'wdot-export-');
        $zip = new ZipArchive();
        $zip->open($tmpFile, ZipArchive::CREATE | ZipArchive::OVERWRITE);
        $zip->addFromString('data.json', (string) json_encode($bundle, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
        $zip->addFromString('README.txt', $this->exportReadme($user->email));
        $zip->close();

        $filename = 'wdot-personal-data-' . now()->format('Ymd-His') . '.zip';

        return response()->streamDownload(static function () use ($tmpFile): void {
            readfile($tmpFile);
            @unlink($tmpFile);
        }, $filename, [
            'Content-Type' => 'application/zip',
        ]);
    }

    /**
     * @OA\Post(
     *   path="/api/v1/me/delete-account",
     *   tags={"Privacy"},
     *   summary="Schedule own account for deletion (30-day grace, PIPA Art. 21)",
     *   security={{"sanctum":{}}},
     *   @OA\RequestBody(@OA\JsonContent(
     *     @OA\Property(property="confirm", type="string", example="DELETE"),
     *     @OA\Property(property="reason",  type="string"),
     *   )),
     *   @OA\Response(response=200, description="Scheduled")
     * )
     */
    public function deleteAccount(Request $request): JsonResponse
    {
        $user = $request->user();
        if (! $user) {
            return response()->json(['error' => 'unauthenticated'], 401);
        }

        $confirm = (string) $request->input('confirm', '');
        if ($confirm !== 'DELETE') {
            return response()->json(['error' => 'confirm_required', 'message' => 'send {"confirm":"DELETE"}'], 422);
        }

        // Mark for deletion: soft-delete + grace_until_at (30d) on a custom column if present.
        $graceUntil = now()->addDays(30);

        if (Schema::hasColumn('users', 'grace_until_at')) {
            $user->forceFill(['grace_until_at' => $graceUntil])->save();
        }
        $user->delete(); // SoftDeletes — sets deleted_at

        $this->audit->log('privacy.account_delete.requested', $user, [
            'reason'      => (string) $request->input('reason', ''),
            'grace_until' => $graceUntil->toIso8601String(),
        ]);

        // Revoke all tokens so the user is signed out immediately.
        $user->tokens()->delete();

        return response()->json([
            'ok'                 => true,
            'message'            => '계정 삭제가 요청되었습니다. 30일 이내 재로그인 시 복구 가능합니다.',
            'grace_until_at'     => $graceUntil->toIso8601String(),
            'hard_delete_after'  => $graceUntil->toIso8601String(),
        ]);
    }

    /**
     * @OA\Get(
     *   path="/api/v1/me/consent",
     *   tags={"Privacy"},
     *   summary="List own consent records",
     *   security={{"sanctum":{}}},
     *   @OA\Response(response=200, description="OK")
     * )
     */
    public function consentIndex(Request $request): JsonResponse
    {
        $user = $request->user();
        if (! $user) {
            return response()->json(['error' => 'unauthenticated'], 401);
        }

        $items = ConsentRecord::query()
            ->where('user_id', $user->id)
            ->orderByDesc('recorded_at')
            ->paginate(50);

        return response()->json([
            'data' => $items->items(),
            'meta' => [
                'total'        => $items->total(),
                'current_page' => $items->currentPage(),
                'per_page'     => $items->perPage(),
                'last_page'    => $items->lastPage(),
            ],
        ]);
    }

    /**
     * @OA\Post(
     *   path="/api/v1/me/consent",
     *   tags={"Privacy"},
     *   summary="Grant or withdraw a consent",
     *   security={{"sanctum":{}}},
     *   @OA\RequestBody(required=true, @OA\JsonContent(
     *     required={"purpose","granted"},
     *     @OA\Property(property="purpose",      type="string", example="marketing"),
     *     @OA\Property(property="granted",      type="boolean"),
     *     @OA\Property(property="jurisdiction", type="string", example="PIPA"),
     *     @OA\Property(property="version",      type="string", example="2026-05-22"),
     *   )),
     *   @OA\Response(response=201, description="Recorded")
     * )
     */
    public function consentStore(Request $request): JsonResponse
    {
        $user = $request->user();
        if (! $user) {
            return response()->json(['error' => 'unauthenticated'], 401);
        }

        $data = $request->validate([
            'purpose'      => 'required|string|max:80',
            'granted'      => 'required|boolean',
            'jurisdiction' => 'sometimes|string|in:PIPA,GDPR,CCPA',
            'version'      => 'sometimes|string|max:30',
        ]);

        $record = ConsentRecord::create([
            'id'           => (string) Str::uuid(),
            'tenant_id'    => $user->tenant_id,
            'user_id'      => $user->id,
            'purpose'      => $data['purpose'],
            'jurisdiction' => $data['jurisdiction'] ?? 'PIPA',
            'version'      => $data['version'] ?? now()->format('Y-m-d'),
            'granted'      => (bool) $data['granted'],
            'ip'           => $request->ip(),
            'ua'           => substr((string) $request->userAgent(), 0, 512),
            'recorded_at'  => now(),
        ]);

        $this->audit->log(
            $data['granted'] ? 'privacy.consent.granted' : 'privacy.consent.withdrawn',
            $user,
            ['purpose' => $data['purpose']]
        );

        return response()->json(['data' => $record], 201);
    }

    /**
     * @OA\Get(
     *   path="/api/v1/me/audit-log",
     *   tags={"Privacy"},
     *   summary="Inspect own audit trail (PIPA Art. 35 access right)",
     *   security={{"sanctum":{}}},
     *   @OA\Response(response=200, description="OK")
     * )
     */
    public function auditLog(Request $request): JsonResponse
    {
        $user = $request->user();
        if (! $user) {
            return response()->json(['error' => 'unauthenticated'], 401);
        }

        $items = AuditLog::query()
            ->where('user_id', $user->id)
            ->orderByDesc('created_at')
            ->paginate(50, ['id', 'action', 'model_type', 'model_id', 'ip', 'ua', 'created_at']);

        return response()->json([
            'data' => $items->items(),
            'meta' => [
                'total'        => $items->total(),
                'current_page' => $items->currentPage(),
                'per_page'     => $items->perPage(),
                'last_page'    => $items->lastPage(),
            ],
        ]);
    }

    private function exportReadme(string $email): string
    {
        $date = now()->toIso8601String();
        return <<<TXT
W·Dot Personal Data Export
==========================
Account : {$email}
Date    : {$date}

This bundle contains every record W·Dot stores that is directly
associated with your account, per Personal Information Protection
Act (PIPA) Art. 35 and GDPR Art. 15 (right of access).

Files
-----
  data.json  Structured dump (profile, consents, posts, comments,
             reactions, audit log).

If anything looks incorrect, contact the data controller for this
tenant.  Account deletion is initiated via
  POST /api/v1/me/delete-account
TXT;
    }
}
