<?php
/**
 * TileProxy_Controller — کاشی‌های نقشه آفلاین
 * مسیر: GET  /acc/v1/tiles/{z}/{x}/{y}
 * مسیر: POST /acc/v1/tiles/download   — پیش‌دانلود محدوده شیراز
 * مسیر: GET  /acc/v1/tiles/status     — وضعیت کش
 */

if ( ! defined( 'ABSPATH' ) ) exit;

class TileProxy_Controller {

    /** پوشه ذخیره کاشی‌ها (زیر uploads) */
    private static function tile_dir(): string {
        return wp_upload_dir()['basedir'] . '/acc-tiles';
    }

    /** URL پوشه کاشی‌ها */
    private static function tile_url_base(): string {
        return wp_upload_dir()['baseurl'] . '/acc-tiles';
    }

    public static function register_routes(): void {
        $ns = 'acc/v1';
        $p  = 'acc_jwt_check_permission';

        register_rest_route( $ns, '/tiles/(?P<z>\d+)/(?P<x>\d+)/(?P<y>\d+)', [
            'methods'             => 'GET',
            'callback'            => [ self::class, 'serve_tile' ],
            'permission_callback' => '__return_true',   // کاشی‌ها عمومی هستند
            'args'                => [
                'z' => [ 'validate_callback' => fn($v) => is_numeric($v) && $v >= 0 && $v <= 20 ],
                'x' => [ 'validate_callback' => fn($v) => is_numeric($v) && $v >= 0 ],
                'y' => [ 'validate_callback' => fn($v) => is_numeric($v) && $v >= 0 ],
            ],
        ] );

        register_rest_route( $ns, '/tiles/download', [
            'methods'             => 'POST',
            'callback'            => [ self::class, 'bulk_download' ],
            'permission_callback' => $p,
        ] );

        register_rest_route( $ns, '/tiles/status', [
            'methods'             => 'GET',
            'callback'            => [ self::class, 'tile_status' ],
            'permission_callback' => $p,
        ] );
    }

    /* ═══════════════════════════════════════════════════════════
       سرو یک کاشی — از کش یا fetch از OSM
    ═══════════════════════════════════════════════════════════ */

    public static function serve_tile( WP_REST_Request $req ) {
        $z = (int) $req['z'];
        $x = (int) $req['x'];
        $y = (int) $req['y'];

        // اعتبارسنجی مختصات tile
        $max_xy = pow( 2, $z );
        if ( $x >= $max_xy || $y >= $max_xy ) {
            return self::serve_blank_tile();
        }

        $path = self::tile_path( $z, $x, $y );

        // اگر در کش موجود است
        if ( file_exists( $path ) ) {
            return self::send_png( $path );
        }

        // تلاش برای دانلود از OpenStreetMap
        $servers  = [ 'a', 'b', 'c' ];
        $s        = $servers[ ($x + $y) % 3 ];
        $osm_url  = "https://{$s}.tile.openstreetmap.org/{$z}/{$x}/{$y}.png";

        $response = wp_remote_get( $osm_url, [
            'timeout'    => 8,
            'user-agent' => 'ACC-Engine/1.0 (+offline-cache)',
            'sslverify'  => false,
        ] );

        if ( ! is_wp_error( $response ) && wp_remote_retrieve_response_code( $response ) === 200 ) {
            $body = wp_remote_retrieve_body( $response );

            // ذخیره در کش
            wp_mkdir_p( dirname( $path ) );
            file_put_contents( $path, $body );

            return self::send_png( $path );
        }

        // آفلاین — برگشت کاشی خاکستری
        return self::serve_blank_tile();
    }

    /* ═══════════════════════════════════════════════════════════
       پیش‌دانلود دسته‌ای برای محدوده شیراز
    ═══════════════════════════════════════════════════════════ */

    public static function bulk_download( WP_REST_Request $req ): WP_REST_Response {
        $lat_min  = (float) ( $req->get_param('lat_min')  ?? 29.35 );
        $lat_max  = (float) ( $req->get_param('lat_max')  ?? 29.85 );
        $lng_min  = (float) ( $req->get_param('lng_min')  ?? 52.25 );
        $lng_max  = (float) ( $req->get_param('lng_max')  ?? 52.95 );
        $zoom_min = (int)   ( $req->get_param('zoom_min') ?? 10     );
        $zoom_max = (int)   ( $req->get_param('zoom_max') ?? 15     );
        $zoom_max = min( $zoom_max, 16 );

        // اندازه محدوده را اعتبارسنجی کن
        $total_estimate = 0;
        for ( $z = $zoom_min; $z <= $zoom_max; $z++ ) {
            [ $x0, $y0 ] = self::lat_lng_to_tile( $lat_max, $lng_min, $z );
            [ $x1, $y1 ] = self::lat_lng_to_tile( $lat_min, $lng_max, $z );
            $total_estimate += ( abs($x1 - $x0) + 1 ) * ( abs($y1 - $y0) + 1 );
        }

        if ( $total_estimate > 8000 ) {
            return new WP_REST_Response( [
                'success' => false,
                'message' => "تعداد کاشی تخمینی ({$total_estimate}) خیلی زیاد است. zoom_max را کاهش دهید.",
            ], 400 );
        }

        // دانلود
        $downloaded = 0;
        $skipped    = 0;
        $failed     = 0;

        for ( $z = $zoom_min; $z <= $zoom_max; $z++ ) {
            [ $x0, $y0 ] = self::lat_lng_to_tile( $lat_max, $lng_min, $z );
            [ $x1, $y1 ] = self::lat_lng_to_tile( $lat_min, $lng_max, $z );
            [ $x0, $x1 ] = [ min($x0,$x1), max($x0,$x1) ];
            [ $y0, $y1 ] = [ min($y0,$y1), max($y0,$y1) ];

            for ( $x = $x0; $x <= $x1; $x++ ) {
                for ( $y = $y0; $y <= $y1; $y++ ) {
                    $path = self::tile_path( $z, $x, $y );

                    if ( file_exists( $path ) ) { $skipped++; continue; }

                    $s   = [ 'a','b','c' ][ ($x+$y) % 3 ];
                    $url = "https://{$s}.tile.openstreetmap.org/{$z}/{$x}/{$y}.png";
                    $res = wp_remote_get( $url, [
                        'timeout'    => 10,
                        'user-agent' => 'ACC-Engine/1.0 (+offline-cache)',
                        'sslverify'  => false,
                    ] );

                    if ( ! is_wp_error( $res ) && wp_remote_retrieve_response_code( $res ) === 200 ) {
                        wp_mkdir_p( dirname( $path ) );
                        file_put_contents( $path, wp_remote_retrieve_body( $res ) );
                        $downloaded++;
                    } else {
                        $failed++;
                    }

                    // کمی استراحت تا سرور OSM اذیت نشود
                    if ( ( $downloaded + $failed ) % 50 === 0 ) {
                        usleep( 200000 ); // 200ms
                    }
                }
            }
        }

        return new WP_REST_Response( [
            'success'    => true,
            'downloaded' => $downloaded,
            'skipped'    => $skipped,
            'failed'     => $failed,
            'total'      => $downloaded + $skipped + $failed,
        ], 200 );
    }

    /* ═══════════════════════════════════════════════════════════
       وضعیت کش
    ═══════════════════════════════════════════════════════════ */

    public static function tile_status( WP_REST_Request $req ): WP_REST_Response {
        $dir   = self::tile_dir();
        $count = 0;
        $size  = 0;

        if ( is_dir( $dir ) ) {
            $iter = new RecursiveIteratorIterator(
                new RecursiveDirectoryIterator( $dir, FilesystemIterator::SKIP_DOTS )
            );
            foreach ( $iter as $f ) {
                if ( $f->getExtension() === 'png' ) {
                    $count++;
                    $size += $f->getSize();
                }
            }
        }

        return new WP_REST_Response( [
            'success'     => true,
            'tile_count'  => $count,
            'size_kb'     => round( $size / 1024 ),
            'cache_dir'   => $dir,
            'is_offline'  => $count > 0,
        ], 200 );
    }

    /* ═══════════════════════════════════════════════════════════
       توابع کمکی
    ═══════════════════════════════════════════════════════════ */

    private static function tile_path( int $z, int $x, int $y ): string {
        return self::tile_dir() . "/{$z}/{$x}/{$y}.png";
    }

    private static function send_png( string $path ) {
        // ارسال فایل PNG به صورت raw (خارج از REST JSON)
        header( 'Content-Type: image/png' );
        header( 'Cache-Control: public, max-age=86400' );
        header( 'X-Tile-Cache: HIT' );
        readfile( $path );
        exit;
    }

    private static function serve_blank_tile() {
        // یک کاشی ۲۵۶×۲۵۶ خاکستری روشن
        if ( function_exists( 'imagecreatetruecolor' ) ) {
            $img = imagecreatetruecolor( 256, 256 );
            $bg  = imagecolorallocate( $img, 230, 230, 220 );
            imagefill( $img, 0, 0, $bg );
            // خطوط grid ظریف
            $grid = imagecolorallocate( $img, 210, 210, 200 );
            for ( $i = 0; $i < 256; $i += 32 ) {
                imageline( $img, $i, 0, $i, 255, $grid );
                imageline( $img, 0, $i, 255, $i, $grid );
            }
            header( 'Content-Type: image/png' );
            header( 'Cache-Control: public, max-age=300' );
            header( 'X-Tile-Cache: BLANK' );
            imagepng( $img );
            imagedestroy( $img );
            exit;
        }

        // GD در دسترس نیست — یک PNG حداقلی ۱×۱
        header( 'Content-Type: image/png' );
        echo base64_decode( 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==' );
        exit;
    }

    /** تبدیل lat/lng به شماره tile */
    private static function lat_lng_to_tile( float $lat, float $lng, int $z ): array {
        $n = pow( 2, $z );
        $x = (int) floor( ( $lng + 180.0 ) / 360.0 * $n );
        $lat_r = deg2rad( $lat );
        $y = (int) floor( ( 1.0 - log( tan( $lat_r ) + 1.0 / cos( $lat_r ) ) / M_PI ) / 2.0 * $n );
        return [ max(0, $x), max(0, $y) ];
    }
}
