<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Str;

class SliderCaptchaController extends Controller
{
    public function init(Request $request)
    {
        $width = 310;
        $height = 155;
        $pieceSize = 60;

        $minX = 80;
        $maxX = $width - $pieceSize - 10;
        $minY = 20;
        $maxY = $height - $pieceSize - 20;

        $x = random_int($minX, $maxX);
        $y = random_int($minY, $maxY);

        $bg = $this->createBackgroundImage($width, $height);
        imagesavealpha($bg, true);
        imagealphablending($bg, true);

        $mask = $this->createJigsawMask($pieceSize);

        // Create piece image (jigsaw-shaped) from bg
        $piece = imagecreatetruecolor($pieceSize, $pieceSize);
        imagesavealpha($piece, true);
        imagealphablending($piece, false);
        $transparent = imagecolorallocatealpha($piece, 0, 0, 0, 127);
        imagefill($piece, 0, 0, $transparent);

        // Visual tuning to mimic common slider/jigsaw captchas
        $pieceTint = 0.55; // 0..1, higher = whiter piece
        $holeTint = 0.78;  // 0..1, higher = whiter hole overlay
        $outlineColorPiece = [255, 255, 255];
        $outlineColorHole = [255, 255, 255];
        $outlineAlpha = 0;

        for ($py = 0; $py < $pieceSize; $py++) {
            for ($px = 0; $px < $pieceSize; $px++) {
                $maskAlpha = (imagecolorat($mask, $px, $py) >> 24) & 0x7F;

                if ($maskAlpha >= 127) {
                    continue;
                }

                $srcColor = imagecolorat($bg, $x + $px, $y + $py);
                $r = ($srcColor >> 16) & 0xFF;
                $g = ($srcColor >> 8) & 0xFF;
                $b = $srcColor & 0xFF;

                // Piece pixel (tinted, keeps some image detail)
                $pr = (int) round($r * (1 - $pieceTint) + 255 * $pieceTint);
                $pg = (int) round($g * (1 - $pieceTint) + 255 * $pieceTint);
                $pb = (int) round($b * (1 - $pieceTint) + 255 * $pieceTint);
                $piecePx = imagecolorallocatealpha($piece, $pr, $pg, $pb, $maskAlpha);

                // Soft drop shadow (draw first, offset)
                $sx = $px + 2;
                $sy = $py + 2;
                if ($sx >= 0 && $sx < $pieceSize && $sy >= 0 && $sy < $pieceSize) {
                    $shadowAlpha = min(127, 92 + (int) floor($maskAlpha / 3));
                    $shadowPx = imagecolorallocatealpha($piece, 0, 0, 0, $shadowAlpha);
                    imagesetpixel($piece, $sx, $sy, $shadowPx);
                }

                imagesetpixel($piece, $px, $py, $piecePx);

                // Hole pixel (opaque whitened photo area, not transparent)
                $hr = (int) round($r * (1 - $holeTint) + 255 * $holeTint);
                $hg = (int) round($g * (1 - $holeTint) + 255 * $holeTint);
                $hb = (int) round($b * (1 - $holeTint) + 255 * $holeTint);
                $holePx = imagecolorallocatealpha($bg, $hr, $hg, $hb, 0);
                imagesetpixel($bg, $x + $px, $y + $py, $holePx);
            }
        }

        // Draw outlines for both hole and piece (edge pixels)
        for ($py = 1; $py < $pieceSize - 1; $py++) {
            for ($px = 1; $px < $pieceSize - 1; $px++) {
                $a = (imagecolorat($mask, $px, $py) >> 24) & 0x7F;
                if ($a >= 127) {
                    continue;
                }

                $n = (imagecolorat($mask, $px, $py - 1) >> 24) & 0x7F;
                $s = (imagecolorat($mask, $px, $py + 1) >> 24) & 0x7F;
                $w = (imagecolorat($mask, $px - 1, $py) >> 24) & 0x7F;
                $e = (imagecolorat($mask, $px + 1, $py) >> 24) & 0x7F;

                $isEdge = ($n >= 127) || ($s >= 127) || ($w >= 127) || ($e >= 127);
                if (! $isEdge) {
                    continue;
                }

                $pieceBorderPx = imagecolorallocatealpha($piece, $outlineColorPiece[0], $outlineColorPiece[1], $outlineColorPiece[2], $outlineAlpha);
                $holeBorderPx = imagecolorallocatealpha($bg, $outlineColorHole[0], $outlineColorHole[1], $outlineColorHole[2], 0);
                imagesetpixel($piece, $px, $py, $pieceBorderPx);
                imagesetpixel($bg, $x + $px, $y + $py, $holeBorderPx);

                // A slightly thicker outline for the hole
                imagesetpixel($bg, $x + $px + 1, $y + $py, $holeBorderPx);
                imagesetpixel($bg, $x + $px - 1, $y + $py, $holeBorderPx);
                imagesetpixel($bg, $x + $px, $y + $py + 1, $holeBorderPx);
                imagesetpixel($bg, $x + $px, $y + $py - 1, $holeBorderPx);
            }
        }

        $token = Str::random(32);
        $payload = [
            'x' => $x,
            'y' => $y,
            'w' => $width,
            'h' => $height,
            's' => $pieceSize,
            'created' => time(),
            'created_ms' => (int) round(microtime(true) * 1000),
        ];

        $request->session()->put('slider_captcha:'.$token, $payload);
        // Also store in cache as a fallback in case the session write/cookie is inconsistent.
        Cache::put('slider_captcha:'.$token, $payload, now()->addMinutes(12));
        // Force session persistence before returning (reduces intermittent "expired" on fast verify)
        $request->session()->save();

        $bgData = $this->toPngDataUri($bg);
        $pieceData = $this->toPngDataUri($piece);

        imagedestroy($mask);
        imagedestroy($bg);
        imagedestroy($piece);

        return response()->json([
            'token' => $token,
            'bg' => $bgData,
            'piece' => $pieceData,
            'y' => $y,
            'w' => $width,
            'h' => $height,
            's' => $pieceSize,
        ]);
    }

    public function verify(Request $request)
    {
        $token = (string) $request->input('token', '');
        $x = (int) $request->input('x', -1);

        if ($token === '' || $x < 0) {
            return response()->json(['ok' => false, 'message' => 'Invalid request.'], 422);
        }

        $challenge = $request->session()->get('slider_captcha:'.$token);
        if (! is_array($challenge)) {
            $challenge = Cache::get('slider_captcha:'.$token);
            if (! is_array($challenge)) {
                return response()->json(['ok' => false, 'message' => 'Captcha expired.'], 422);
            }
        }

        $created = (int) ($challenge['created'] ?? 0);
        if ($created <= 0 || (time() - $created) > 10 * 60) {
            $request->session()->forget('slider_captcha:'.$token);
            Cache::forget('slider_captcha:'.$token);

            return response()->json(['ok' => false, 'message' => 'Captcha expired.'], 422);
        }

        $expectedX = (int) ($challenge['x'] ?? -999);
        $tolerance = 10;

        if (abs($expectedX - $x) > $tolerance) {
            return response()->json(['ok' => false, 'message' => 'Verification failed.'], 422);
        }

        // Mark as verified (single-use)
        $verifyToken = Str::random(40);
        $request->session()->put('slider_captcha_verified:'.$verifyToken, time());

        // Prevent re-use of challenge token
        $request->session()->forget('slider_captcha:'.$token);
        Cache::forget('slider_captcha:'.$token);

        return response()->json(['ok' => true, 'verifyToken' => $verifyToken]);
    }

    private function toPngDataUri($image): string
    {
        ob_start();
        imagepng($image);
        $png = ob_get_clean();

        if (! is_string($png)) {
            return '';
        }

        return 'data:image/png;base64,'.base64_encode($png);
    }

    private function createBackgroundImage(int $width, int $height)
    {
        $candidates = [];

        // Prefer stock/banner-like images already shipped with the app
        //$candidates = array_merge($candidates, glob(public_path('uploads/eventbanners/*.jpg')) ?: []);
        //$candidates = array_merge($candidates, glob(public_path('uploads/surveybanners/*.jpg')) ?: []);

        // Fallback to a couple of theme images
        //$candidates[] = public_path('themes/idh/default/design/img/banner.jpg');
        //$candidates[] = public_path('themes/idh/default/design/img/cycle.jpg');
		
		$candidates = array_merge($candidates, glob(public_path('images/*.jpg')) ?: []);

        $existing = array_values(array_filter($candidates, function ($p) {
            return is_string($p) && $p !== '' && file_exists($p);
        }));

        if (! empty($existing)) {
            $path = $existing[array_rand($existing)];

            $ext = strtolower((string) pathinfo($path, PATHINFO_EXTENSION));
            $src = false;
            if ($ext === 'png') {
                $src = @imagecreatefrompng($path);
            } else {
                $src = @imagecreatefromjpeg($path);
            }

            if ($src !== false) {
                $srcW = imagesx($src);
                $srcH = imagesy($src);

                $dst = imagecreatetruecolor($width, $height);
                imagealphablending($dst, true);
                imagesavealpha($dst, true);

                // Cover crop
                $scale = max($width / max(1, $srcW), $height / max(1, $srcH));
                $newW = (int) ceil($srcW * $scale);
                $newH = (int) ceil($srcH * $scale);
                $dstX = (int) floor(($width - $newW) / 2);
                $dstY = (int) floor(($height - $newH) / 2);

                imagecopyresampled($dst, $src, $dstX, $dstY, 0, 0, $newW, $newH, $srcW, $srcH);
                imagedestroy($src);

                return $dst;
            }
        }

        // Fallback: simple generated background
        $dst = imagecreatetruecolor($width, $height);
        imagesavealpha($dst, true);
        imagealphablending($dst, true);
        $bgColor = imagecolorallocate($dst, random_int(210, 235), random_int(210, 235), random_int(210, 235));
        imagefill($dst, 0, 0, $bgColor);
        for ($i = 0; $i < 16; $i++) {
            $c = imagecolorallocatealpha($dst, random_int(80, 180), random_int(80, 180), random_int(80, 180), random_int(75, 105));
            imagefilledellipse($dst, random_int(0, $width), random_int(0, $height), random_int(18, 55), random_int(18, 55), $c);
        }

        return $dst;
    }

    private function createJigsawMask(int $size)
    {
        $mask = imagecreatetruecolor($size, $size);
        imagesavealpha($mask, true);
        imagealphablending($mask, false);

        $transparent = imagecolorallocatealpha($mask, 0, 0, 0, 127);
        imagefill($mask, 0, 0, $transparent);

        $solid = imagecolorallocatealpha($mask, 255, 255, 255, 0);

        if (function_exists('imageantialias')) {
            @imageantialias($mask, true);
        }

        $mid = (int) floor($size / 2);
        $pad = (int) max(5, floor($size / 10));
        $cornerR = (int) max(6, floor($size / 10));
        $knobR = (int) max(9, floor($size / 6));

        $left = $pad;
        $top = $pad;
        $right = $size - $pad - 1;
        $bottom = $size - $pad - 1;

        // Rounded rectangle base
        imagefilledrectangle($mask, $left + $cornerR, $top, $right - $cornerR, $bottom, $solid);
        imagefilledrectangle($mask, $left, $top + $cornerR, $right, $bottom - $cornerR, $solid);
        imagefilledellipse($mask, $left + $cornerR, $top + $cornerR, $cornerR * 2, $cornerR * 2, $solid);
        imagefilledellipse($mask, $right - $cornerR, $top + $cornerR, $cornerR * 2, $cornerR * 2, $solid);
        imagefilledellipse($mask, $left + $cornerR, $bottom - $cornerR, $cornerR * 2, $cornerR * 2, $solid);
        imagefilledellipse($mask, $right - $cornerR, $bottom - $cornerR, $cornerR * 2, $cornerR * 2, $solid);

        // Tabs: top, right, bottom
        imagefilledellipse($mask, $mid, $top, $knobR * 2, $knobR * 2, $solid);
        imagefilledellipse($mask, $right, $mid, $knobR * 2, $knobR * 2, $solid);
        imagefilledellipse($mask, $mid, $bottom, $knobR * 2, $knobR * 2, $solid);

        // Indent: left
        imagefilledellipse($mask, $left, $mid, ($knobR - 1) * 2, ($knobR - 1) * 2, $transparent);

        return $mask;
    }
}
