<?php
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Headers: Content-Type, Authorization, Accept');
header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS');
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') { http_response_code(204); exit; }

require __DIR__.'/../src/db.php';
require __DIR__.'/../src/helpers.php';
require __DIR__.'/../src/auth_schema.php';

$method = $_SERVER['REQUEST_METHOD'];
if (isset($_GET['route'])) {
    $path = '/' . trim((string)$_GET['route'], '/');
} else {
    $uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH) ?: '/';
    $script = $_SERVER['SCRIPT_NAME'] ?? '';
    $base = rtrim(dirname($script), '/');
    if ($base !== '' && strpos($uri, $base) === 0) $uri = substr($uri, strlen($base));
    if (strpos($uri, '/index.php') === 0) $uri = substr($uri, strlen('/index.php'));
    $path = '/' . trim($uri, '/');
}

function app_settings(PDO $pdo): array {
    $defaults = [
        'id'=>1,
        'organization_name'=>'সাদাকা ব্লাড সোসাইটি ময়মনসিংহ',
        'tagline'=>'রক্তের বন্ধনে জুড়ুক প্রাণ, সাদাকা হোক রক্তদান',
        'primary_color'=>'#C62828',
        'emergency_phone'=>'',
    ];
    $row = $pdo->query('SELECT * FROM app_settings WHERE id=1 LIMIT 1')->fetch();
    if (is_array($row)) return array_merge($defaults, $row);
    try {
        $stmt=$pdo->prepare('INSERT INTO app_settings(id,organization_name,tagline,primary_color,emergency_phone) VALUES(1,?,?,?,?)');
        $stmt->execute([$defaults['organization_name'],$defaults['tagline'],$defaults['primary_color'],$defaults['emergency_phone']]);
    } catch(Throwable $ignored) {}
    $row=$pdo->query('SELECT * FROM app_settings WHERE id=1 LIMIT 1')->fetch();
    return is_array($row) ? array_merge($defaults,$row) : $defaults;
}


function banner_media_url(string $filename): string {
    return app_public_base_url().'/api/public/index.php?route=media%2Fbanner&file='.rawurlencode($filename);
}

function ensure_banner_schema(PDO $pdo): void {
    $pdo->exec("CREATE TABLE IF NOT EXISTS banners (
        id INT AUTO_INCREMENT PRIMARY KEY,
        title VARCHAR(190) NOT NULL DEFAULT '',
        subtitle VARCHAR(255) NULL,
        image_url TEXT NULL,
        link_url TEXT NULL,
        is_active TINYINT(1) NOT NULL DEFAULT 1,
        sort_order INT NOT NULL DEFAULT 0,
        created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
        INDEX idx_banners_active_sort (is_active, sort_order, id)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci");

    if (!column_exists($pdo,'banners','link_url')) {
        $pdo->exec("ALTER TABLE banners ADD COLUMN link_url TEXT NULL AFTER image_url");
    }
    if (!column_exists($pdo,'banners','sort_order')) {
        $pdo->exec("ALTER TABLE banners ADD COLUMN sort_order INT NOT NULL DEFAULT 0 AFTER is_active");
    }
    if (!column_exists($pdo,'banners','is_active')) {
        $pdo->exec("ALTER TABLE banners ADD COLUMN is_active TINYINT(1) NOT NULL DEFAULT 1 AFTER link_url");
    }

    $charsetStmt=$pdo->prepare("SELECT CCSA.character_set_name FROM information_schema.TABLES T JOIN information_schema.COLLATION_CHARACTER_SET_APPLICABILITY CCSA ON CCSA.collation_name=T.table_collation WHERE T.table_schema=DATABASE() AND T.table_name='banners' LIMIT 1");
    $charsetStmt->execute();
    $bannerCharset=strtolower((string)($charsetStmt->fetchColumn()?:''));
    if ($bannerCharset!=='' && $bannerCharset!=='utf8mb4') {
        $pdo->exec("ALTER TABLE banners CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci");
    }

    // Give a fresh installation one working SQL/database-driven demo banner.
    // It is inserted only when no banner exists, so existing admin data is never replaced.
    $count=(int)$pdo->query('SELECT COUNT(*) FROM banners')->fetchColumn();
    $demoFile=__DIR__.'/uploads/banners/demo_banner.png';
    if ($count===0 && is_file($demoFile)) {
        $stmt=$pdo->prepare('INSERT INTO banners(title,subtitle,image_url,link_url,is_active,sort_order) VALUES(?,?,?,?,1,1)');
        $stmt->execute([
            'লাইফড্রপ',
            'রক্ত দিন, জীবন বাঁচান',
            banner_media_url('demo_banner.png'),
            '',
        ]);
    }
}

function ensure_filter_schema(PDO $pdo): void {
    $pdo->exec("CREATE TABLE IF NOT EXISTS districts (
        id INT AUTO_INCREMENT PRIMARY KEY,
        name VARCHAR(120) NOT NULL UNIQUE,
        sort_order INT NOT NULL DEFAULT 0,
        is_active TINYINT(1) NOT NULL DEFAULT 1,
        created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
        INDEX idx_districts_active (is_active, sort_order)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci");
    $pdo->exec("CREATE TABLE IF NOT EXISTS thanas (
        id INT AUTO_INCREMENT PRIMARY KEY,
        district_id INT NOT NULL,
        name VARCHAR(120) NOT NULL,
        sort_order INT NOT NULL DEFAULT 0,
        is_active TINYINT(1) NOT NULL DEFAULT 1,
        created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
        UNIQUE KEY uq_thana_district_name (district_id, name),
        INDEX idx_thanas_district (district_id, is_active, sort_order)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci");
    $pdo->exec("CREATE TABLE IF NOT EXISTS blood_groups (
        id INT AUTO_INCREMENT PRIMARY KEY,
        name VARCHAR(5) NOT NULL UNIQUE,
        sort_order INT NOT NULL DEFAULT 0,
        is_active TINYINT(1) NOT NULL DEFAULT 1,
        created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
        INDEX idx_blood_groups_active (is_active, sort_order)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci");

    $stmt=$pdo->prepare("INSERT INTO districts(name,sort_order,is_active) VALUES(?,1,1) ON DUPLICATE KEY UPDATE is_active=1");
    $stmt->execute(['ময়মনসিংহ']);
    $districtId=(int)$pdo->query("SELECT id FROM districts WHERE name='ময়মনসিংহ' LIMIT 1")->fetchColumn();
    if ($districtId>0) {
        $stmt=$pdo->prepare("INSERT INTO thanas(district_id,name,sort_order,is_active) VALUES(?,?,1,1) ON DUPLICATE KEY UPDATE is_active=1");
        $stmt->execute([$districtId,'ময়মনসিংহ সদর']);
    }
    $groups=['A+','B+','O+','AB+','A-','B-','O-','AB-'];
    $stmt=$pdo->prepare("INSERT INTO blood_groups(name,sort_order,is_active) VALUES(?,?,1) ON DUPLICATE KEY UPDATE is_active=1,sort_order=VALUES(sort_order)");
    foreach($groups as $i=>$group) $stmt->execute([$group,$i+1]);
}

function catalog_name_by_id(PDO $pdo, string $table, int $id): ?string {
    if (!in_array($table,['districts','thanas','blood_groups'],true) || $id<=0) return null;
    $s=$pdo->prepare("SELECT name FROM {$table} WHERE id=? LIMIT 1");
    $s->execute([$id]);
    $name=$s->fetchColumn();
    return $name===false ? null : (string)$name;
}

function donor_filter_payload(PDO $pdo, bool $onlyActive=true): array {
    $active=$onlyActive ? ' WHERE is_active=1' : '';
    $districts=$pdo->query("SELECT id,name,sort_order,is_active FROM districts{$active} ORDER BY sort_order,name,id")->fetchAll();
    $thanaSql='SELECT t.id,t.district_id,t.name,t.sort_order,t.is_active,d.name AS district_name FROM thanas t JOIN districts d ON d.id=t.district_id';
    if ($onlyActive) $thanaSql.=' WHERE t.is_active=1 AND d.is_active=1';
    $thanaSql.=' ORDER BY d.sort_order,t.sort_order,t.name,t.id';
    $thanas=$pdo->query($thanaSql)->fetchAll();
    $byDistrict=[];
    foreach($thanas as $t) $byDistrict[(string)$t['district_id']][]=$t;
    foreach($districts as &$d) $d['thanas']=$byDistrict[(string)$d['id']]??[];
    unset($d);
    $groups=$pdo->query("SELECT id,name,sort_order,is_active FROM blood_groups{$active} ORDER BY sort_order,name,id")->fetchAll();
    return ['districts'=>$districts,'blood_groups'=>$groups];
}

function donor_application_row(PDO $pdo, int $userId): ?array {
    $s=$pdo->prepare('SELECT da.*,u.photo_url,u.email FROM donor_applications da JOIN users u ON u.id=da.user_id WHERE da.user_id=? LIMIT 1');
    $s->execute([$userId]);
    $row=$s->fetch();
    return is_array($row) ? $row : null;
}

function resolve_donor_application_input(PDO $pdo, array $u, array $d): array {
    $name=clean_string($d['name']??($u['name']??''),160);
    $phone=clean_string($d['phone']??($u['phone']??''),40);
    if($name===''||$phone==='') json_response(['message'=>'নাম ও মোবাইল নম্বর প্রয়োজন'],422);

    $bloodGroup='';
    if(!empty($d['blood_group_id'])) {
        $bloodGroup=catalog_name_by_id($pdo,'blood_groups',(int)$d['blood_group_id'])??'';
    } else {
        $bloodGroup=clean_string($d['blood_group']??'',5);
    }
    if($bloodGroup==='') json_response(['message'=>'রক্তের গ্রুপ নির্বাচন করুন'],422);

    $districtId=(int)($d['district_id']??0);
    $thanaId=(int)($d['thana_id']??0);
    if($districtId<=0 || $thanaId<=0) json_response(['message'=>'জেলা ও থানা নির্বাচন করুন'],422);

    $q=$pdo->prepare('SELECT t.id,t.name AS thana_name,t.district_id,d.name AS district_name FROM thanas t JOIN districts d ON d.id=t.district_id WHERE t.id=? AND t.is_active=1 AND d.is_active=1 LIMIT 1');
    $q->execute([$thanaId]);
    $loc=$q->fetch();
    if(!$loc) json_response(['message'=>'সঠিক থানা নির্বাচন করুন'],422);
    if((int)$loc['district_id']!==$districtId) json_response(['message'=>'থানাটি নির্বাচিত জেলার অধীনে নয়'],422);

    $lastDonation=trim((string)($d['last_donation_date']??''));
    if($lastDonation!=='' && !preg_match('/^\d{4}-\d{2}-\d{2}$/',$lastDonation)) {
        json_response(['message'=>'শেষ রক্তদানের তারিখ YYYY-MM-DD ফরম্যাটে দিন'],422);
    }

    return [
        'name'=>$name,
        'phone'=>$phone,
        'blood_group'=>$bloodGroup,
        'district_id'=>$districtId,
        'district'=>(string)$loc['district_name'],
        'thana_id'=>$thanaId,
        'upazila'=>(string)$loc['thana_name'],
        'address'=>clean_string($d['address']??'',255),
        'last_donation_date'=>$lastDonation!==''?$lastDonation:null,
        'available'=>bool_int($d['available']??1),
    ];
}

function sync_approved_donor(PDO $pdo, array $application): int {
    $userId=(int)$application['user_id'];
    $user=$pdo->prepare('SELECT photo_url FROM users WHERE id=? LIMIT 1');
    $user->execute([$userId]);
    $photo=clean_string($user->fetchColumn()?:'',1000);

    $find=$pdo->prepare('SELECT id FROM donors WHERE user_id=? ORDER BY id ASC LIMIT 1');
    $find->execute([$userId]);
    $donorId=(int)($find->fetchColumn()?:0);
    if($donorId<=0) {
        // Reuse an old donor row with the same phone when possible so demo/legacy data is not duplicated.
        $find=$pdo->prepare('SELECT id FROM donors WHERE phone=? ORDER BY id ASC LIMIT 1');
        $find->execute([(string)$application['phone']]);
        $donorId=(int)($find->fetchColumn()?:0);
    }

    $values=[
        (string)$application['name'],(string)$application['phone'],(string)$application['blood_group'],
        (string)$application['district'],(string)$application['upazila'],(string)($application['address']??''),
        $photo!==''?$photo:null,$application['last_donation_date']??null,(int)($application['available']??1),1,
    ];
    if($donorId>0) {
        $sql='UPDATE donors SET user_id=?,name=?,phone=?,blood_group=?,district=?,upazila=?,address=?,photo_url=?,last_donation_date=?,available=?,verified=? WHERE id=?';
        $pdo->prepare($sql)->execute(array_merge([$userId],$values,[$donorId]));
        return $donorId;
    }

    $sql='INSERT INTO donors(user_id,name,phone,blood_group,district,upazila,address,photo_url,last_donation_date,available,verified) VALUES(?,?,?,?,?,?,?,?,?,?,?)';
    $pdo->prepare($sql)->execute(array_merge([$userId],$values));
    return (int)$pdo->lastInsertId();
}

try {
    // CORS-safe profile image endpoint for Flutter Web / Chrome.
    if ($method==='GET' && $path==='/media/profile') {
        $file=basename((string)($_GET['file']??''));
        if ($file==='' || !preg_match('/^[A-Za-z0-9._-]+\.(?:jpe?g|png|webp)$/i',$file)) {
            http_response_code(404); exit;
        }
        $full=__DIR__.'/uploads/profile/'.$file;
        if (!is_file($full) || !is_readable($full)) {
            http_response_code(404); exit;
        }
        $ext=strtolower(pathinfo($file,PATHINFO_EXTENSION));
        $mime=match($ext){'jpg','jpeg'=>'image/jpeg','png'=>'image/png','webp'=>'image/webp',default=>'application/octet-stream'};
        header('Content-Type: '.$mime);
        header('Content-Length: '.filesize($full));
        header('Cache-Control: public, max-age=31536000, immutable');
        header('Cross-Origin-Resource-Policy: cross-origin');
        header('X-Content-Type-Options: nosniff');
        readfile($full);
        exit;
    }

    // CORS-safe banner image endpoint used by Home and the Flutter admin panel.
    if ($method==='GET' && $path==='/media/banner') {
        $file=basename((string)($_GET['file']??''));
        if ($file==='' || !preg_match('/^[A-Za-z0-9._-]+\.(?:jpe?g|png|webp)$/i',$file)) {
            http_response_code(404); exit;
        }
        $full=__DIR__.'/uploads/banners/'.$file;
        if (!is_file($full) || !is_readable($full)) {
            http_response_code(404); exit;
        }
        $ext=strtolower(pathinfo($file,PATHINFO_EXTENSION));
        $mime=match($ext){'jpg','jpeg'=>'image/jpeg','png'=>'image/png','webp'=>'image/webp',default=>'application/octet-stream'};
        header('Content-Type: '.$mime);
        header('Content-Length: '.filesize($full));
        header('Cache-Control: public, max-age=86400');
        header('Cross-Origin-Resource-Policy: cross-origin');
        header('X-Content-Type-Options: nosniff');
        readfile($full);
        exit;
    }

    // Auth/user/admin routes auto-create the required auth schema after extraction.
    if (str_starts_with($path, '/auth') || str_starts_with($path, '/admin') || $path === '/requests') {
        ensure_auth_schema(db());
    }
    if ($path==='/donors' || $path==='/donor-filters' || str_starts_with($path,'/admin') || $path==='/auth/donor-application') {
        ensure_filter_schema(db());
    }
    if ($path==='/home' || str_starts_with($path,'/admin/banners')) {
        ensure_banner_schema(db());
    }
    if ($path==='/donors' || $path==='/auth/donor-application' || str_starts_with($path,'/admin/donor-applications') || $path==='/admin/dashboard') {
        ensure_donor_application_schema(db());
    }

    if ($method==='GET' && $path==='/health') {
        json_response(['ok'=>true,'time'=>date(DATE_ATOM)]);
    }

    if ($method==='GET' && $path==='/home') {
        $pdo=db();
        $settings=app_settings($pdo);
        $banners=$pdo->query('SELECT * FROM banners WHERE is_active=1 ORDER BY sort_order,id DESC')->fetchAll();
        $features=$pdo->query('SELECT * FROM feature_cards WHERE is_active=1 ORDER BY section_key,sort_order,id')->fetchAll();
        $stats=[
            'donors'=>(int)$pdo->query('SELECT COUNT(*) FROM donors')->fetchColumn(),
            'active_requests'=>(int)$pdo->query("SELECT COUNT(*) FROM blood_requests WHERE status IN ('pending','approved')")->fetchColumn(),
            'successful_donations'=>(int)$pdo->query("SELECT COUNT(*) FROM blood_requests WHERE status='completed'")->fetchColumn(),
        ];
        json_response(compact('settings','banners','features','stats'));
    }

    if ($method==='GET' && $path==='/donor-filters') {
        json_response(donor_filter_payload(db(),true));
    }

    if ($method==='GET' && $path==='/donors') {
        $pdo=db();$where=['1=1'];$args=[];

        $bloodGroup='';
        if (!empty($_GET['blood_group_id'])) {
            $bloodGroup=catalog_name_by_id($pdo,'blood_groups',(int)$_GET['blood_group_id'])??'';
            if ($bloodGroup==='') json_response(['message'=>'Invalid blood group'],422);
        } elseif (!empty($_GET['blood_group'])) {
            $bloodGroup=clean_string($_GET['blood_group'],5);
        }
        if ($bloodGroup!=='') {$where[]='blood_group=?';$args[]=$bloodGroup;}

        $district='';
        if (!empty($_GET['district_id'])) {
            $district=catalog_name_by_id($pdo,'districts',(int)$_GET['district_id'])??'';
            if ($district==='') json_response(['message'=>'Invalid district'],422);
        } elseif (!empty($_GET['district'])) {
            $district=clean_string($_GET['district'],100);
        }
        if ($district!=='') {$where[]='district=?';$args[]=$district;}

        $thana='';
        if (!empty($_GET['thana_id'])) {
            $thanaId=(int)$_GET['thana_id'];
            $s=$pdo->prepare('SELECT t.name,t.district_id,d.name AS district_name FROM thanas t JOIN districts d ON d.id=t.district_id WHERE t.id=? LIMIT 1');
            $s->execute([$thanaId]);$row=$s->fetch();
            if (!$row) json_response(['message'=>'Invalid thana'],422);
            if (!empty($_GET['district_id']) && (int)$row['district_id']!==(int)$_GET['district_id']) json_response(['message'=>'Thana does not belong to selected district'],422);
            $thana=(string)$row['name'];
            if ($district==='') {$district=(string)$row['district_name'];$where[]='district=?';$args[]=$district;}
        } elseif (!empty($_GET['thana'])) {
            $thana=clean_string($_GET['thana'],100);
        } elseif (!empty($_GET['upazila'])) {
            $thana=clean_string($_GET['upazila'],100);
        }
        if ($thana!=='') {$where[]='upazila=?';$args[]=$thana;}

        if (!empty($_GET['q'])) {
            $where[]='(name LIKE ? OR district LIKE ? OR upazila LIKE ? OR blood_group LIKE ?)';
            $q='%'.clean_string($_GET['q'],120).'%'; array_push($args,$q,$q,$q,$q);
        }

        // Server-side pagination: 10 donors per page by default.
        // This lets Flutter replace only the result list without reloading/navigating the page.
        $page=max(1,(int)($_GET['page']??1));
        $perPage=(int)($_GET['per_page']??10);
        if ($perPage<1) $perPage=10;
        if ($perPage>50) $perPage=50;

        $whereSql=implode(' AND ',$where);
        $countStmt=$pdo->prepare('SELECT COUNT(*) FROM donors WHERE '.$whereSql);
        $countStmt->execute($args);
        $total=(int)$countStmt->fetchColumn();
        $totalPages=max(1,(int)ceil($total/$perPage));
        if ($page>$totalPages) $page=$totalPages;
        $offset=($page-1)*$perPage;

        // Prefer the linked user's latest profile photo. If user_id was not linked on an older donor row,
        // fall back to matching the same phone number. This keeps Home, search and details photos in sync.
        $sql="SELECT d.*, COALESCE(
"
            ."NULLIF((SELECT u1.photo_url FROM users u1 WHERE u1.id=d.user_id LIMIT 1), ''), 
"
            ."NULLIF((SELECT u2.photo_url FROM users u2 WHERE u2.phone=d.phone LIMIT 1), ''), 
"
            ."d.photo_url) AS photo_url, 
"
            ."CASE WHEN d.last_donation_date IS NULL OR DATEDIFF(CURDATE(),d.last_donation_date)>=120 THEN 1 ELSE 0 END AS can_donate 
"
            ."FROM donors d WHERE ".$whereSql." ORDER BY d.available DESC, d.verified DESC, d.id DESC LIMIT ".$perPage." OFFSET ".$offset;
        $s=$pdo->prepare($sql);$s->execute($args);
        json_response([
            'data'=>$s->fetchAll(),
            'pagination'=>[
                'page'=>$page,
                'per_page'=>$perPage,
                'total'=>$total,
                'total_pages'=>$totalPages,
                'has_previous'=>$page>1,
                'has_next'=>$page<$totalPages,
            ],
        ]);
    }

    if ($method==='GET' && $path==='/requests') {
        $s=db()->query("SELECT r.*,u.name AS user_name FROM blood_requests r LEFT JOIN users u ON u.id=r.user_id WHERE r.status IN ('pending','approved') ORDER BY r.donation_date ASC,r.id DESC LIMIT 200");
        json_response(['data'=>$s->fetchAll()]);
    }

    // ---------------- USER AUTH ----------------
    if ($method==='POST' && $path==='/auth/register') {
        $d=input_json();
        $name=clean_string($d['name']??'',160);
        $phone=clean_string($d['phone']??'',40);
        $email=strtolower(clean_string($d['email']??'',190));
        $password=(string)($d['password']??'');
        if ($name==='' || $phone==='' || $password==='') json_response(['message'=>'নাম, মোবাইল ও পাসওয়ার্ড দিন'],422);
        if (strlen($password)<6) json_response(['message'=>'পাসওয়ার্ড কমপক্ষে ৬ অক্ষরের হতে হবে'],422);
        if ($email!=='' && !filter_var($email,FILTER_VALIDATE_EMAIL)) json_response(['message'=>'সঠিক ইমেইল দিন'],422);

        $check=db()->prepare('SELECT id FROM users WHERE phone=? OR (?<>\'\' AND email=?) LIMIT 1');
        $check->execute([$phone,$email,$email]);
        if ($check->fetch()) json_response(['message'=>'এই মোবাইল বা ইমেইল দিয়ে একাউন্ট আগে থেকেই আছে'],409);

        $token=bin2hex(random_bytes(32));
        $stmt=db()->prepare('INSERT INTO users(name,email,phone,password_hash,blood_group,district,status,verified,token,last_login_at) VALUES(?,?,?,?,?,?,\'active\',0,?,NOW())');
        $stmt->execute([
            $name,$email!==''?$email:null,$phone,password_hash($password,PASSWORD_DEFAULT),
            clean_string($d['blood_group']??'',5) ?: null,
            clean_string($d['district']??'',100) ?: null,
            $token,
        ]);
        $id=(int)db()->lastInsertId();
        $s=db()->prepare('SELECT * FROM users WHERE id=?');$s->execute([$id]);$u=$s->fetch();
        json_response(['message'=>'রেজিস্ট্রেশন সফল','token'=>$token,'user'=>public_user($u)],201);
    }

    if ($method==='POST' && $path==='/auth/login') {
        $d=input_json();
        $identity=clean_string($d['identity']??($d['phone']??($d['email']??'')),190);
        $password=(string)($d['password']??'');
        if ($identity==='' || $password==='') json_response(['message'=>'মোবাইল/ইমেইল ও পাসওয়ার্ড দিন'],422);
        $s=db()->prepare('SELECT * FROM users WHERE phone=? OR email=? LIMIT 1');$s->execute([$identity,strtolower($identity)]);$u=$s->fetch();
        if (!$u || !password_verify($password,$u['password_hash'])) json_response(['message'=>'মোবাইল/ইমেইল অথবা পাসওয়ার্ড সঠিক নয়'],401);
        if (($u['status']??'active')!=='active') json_response(['message'=>'আপনার একাউন্টটি অ্যাডমিন ব্লক করেছেন'],403);
        $token=bin2hex(random_bytes(32));
        db()->prepare('UPDATE users SET token=?,last_login_at=NOW() WHERE id=?')->execute([$token,$u['id']]);
        $s=db()->prepare('SELECT * FROM users WHERE id=?');$s->execute([$u['id']]);$u=$s->fetch();
        json_response(['message'=>'লগইন সফল','token'=>$token,'user'=>public_user($u)]);
    }

    if ($method==='GET' && $path==='/auth/me') {
        $u=require_user();
        json_response(['user'=>public_user($u)]);
    }


    if ($method==='GET' && $path==='/auth/donor-application') {
        $u=require_user();
        $pdo=db();
        ensure_donor_application_schema($pdo);
        ensure_filter_schema($pdo);
        json_response([
            'application'=>donor_application_row($pdo,(int)$u['id']),
            'filters'=>donor_filter_payload($pdo,true),
        ]);
    }

    if (($method==='POST' || $method==='PUT') && $path==='/auth/donor-application') {
        $u=require_user();
        $pdo=db();
        ensure_donor_application_schema($pdo);
        ensure_filter_schema($pdo);
        $d=input_json();
        $payload=resolve_donor_application_input($pdo,$u,$d);
        $existing=donor_application_row($pdo,(int)$u['id']);
        $status=$existing ? (string)$existing['status'] : 'pending';
        if($status==='rejected') $status='pending';
        if(!in_array($status,['pending','approved'],true)) $status='pending';

        $pdo->beginTransaction();
        try {
            $sql="INSERT INTO donor_applications(user_id,name,phone,blood_group,district_id,district,thana_id,upazila,address,last_donation_date,available,status,admin_note,reviewed_at)
"
                ."VALUES(?,?,?,?,?,?,?,?,?,?,?,?,NULL,NULL)
"
                ."ON DUPLICATE KEY UPDATE name=VALUES(name),phone=VALUES(phone),blood_group=VALUES(blood_group),district_id=VALUES(district_id),district=VALUES(district),thana_id=VALUES(thana_id),upazila=VALUES(upazila),address=VALUES(address),last_donation_date=VALUES(last_donation_date),available=VALUES(available),status=VALUES(status),admin_note=IF(VALUES(status)='pending',NULL,admin_note),reviewed_at=IF(VALUES(status)='pending',NULL,reviewed_at)";
            $pdo->prepare($sql)->execute([
                (int)$u['id'],$payload['name'],$payload['phone'],$payload['blood_group'],$payload['district_id'],$payload['district'],
                $payload['thana_id'],$payload['upazila'],$payload['address']?:null,$payload['last_donation_date'],$payload['available'],$status,
            ]);
            $app=donor_application_row($pdo,(int)$u['id']);
            if($app && $status==='approved') sync_approved_donor($pdo,$app);
            $pdo->commit();
        } catch(Throwable $e) {
            if($pdo->inTransaction()) $pdo->rollBack();
            throw $e;
        }
        $app=donor_application_row($pdo,(int)$u['id']);
        $message=$status==='approved' ? 'ডোনার তথ্য আপডেট হয়েছে' : ($existing ? 'আবেদনের তথ্য আপডেট হয়েছে' : 'ডোনার আবেদন জমা হয়েছে');
        json_response(['message'=>$message,'application'=>$app],$existing?200:201);
    }

    if ($method==='POST' && $path==='/auth/logout') {
        $u=require_user();
        db()->prepare('UPDATE users SET token=NULL WHERE id=?')->execute([$u['id']]);
        json_response(['message'=>'লগআউট হয়েছে']);
    }

    if ($method==='POST' && $path==='/auth/photo') {
        $u=require_user();
        if (!isset($_FILES['photo']) || !is_array($_FILES['photo'])) {
            json_response(['message'=>'ছবি নির্বাচন করুন'],422);
        }
        $file=$_FILES['photo'];
        $uploadError=(int)($file['error']??UPLOAD_ERR_NO_FILE);
        if ($uploadError!==UPLOAD_ERR_OK) {
            $message=$uploadError===UPLOAD_ERR_INI_SIZE || $uploadError===UPLOAD_ERR_FORM_SIZE
                ? 'ছবির সাইজ সার্ভারের অনুমোদিত সীমার বেশি'
                : 'ছবি আপলোড করা যায়নি';
            json_response(['message'=>$message],422);
        }
        $size=(int)($file['size']??0);
        if ($size<=0 || $size>5*1024*1024) {
            json_response(['message'=>'ছবির সাইজ সর্বোচ্চ ৫ MB হতে পারবে'],422);
        }
        $tmp=(string)($file['tmp_name']??'');
        if ($tmp==='' || !is_uploaded_file($tmp)) {
            json_response(['message'=>'সঠিক upload file পাওয়া যায়নি'],422);
        }

        $imageInfo=@getimagesize($tmp);
        $mime=is_array($imageInfo) ? (string)($imageInfo['mime']??'') : '';
        $extensions=[
            'image/jpeg'=>'jpg',
            'image/png'=>'png',
            'image/webp'=>'webp',
        ];
        if (!isset($extensions[$mime])) {
            json_response(['message'=>'শুধু JPG, PNG অথবা WEBP ছবি আপলোড করা যাবে'],422);
        }

        $uploadDir=__DIR__.'/uploads/profile';
        if (!is_dir($uploadDir) && !mkdir($uploadDir,0755,true) && !is_dir($uploadDir)) {
            json_response(['message'=>'Server upload folder তৈরি করা যায়নি'],500);
        }
        $filename='user_'.(int)$u['id'].'_'.bin2hex(random_bytes(8)).'.'.$extensions[$mime];
        $destination=$uploadDir.'/'.$filename;
        if (!move_uploaded_file($tmp,$destination)) {
            json_response(['message'=>'ছবি server-এ save করা যায়নি'],500);
        }

        $cfg=require __DIR__.'/../config/config.php';
        $base=rtrim((string)($cfg['base_url']??''),'/');
        if ($base==='') {
            $scheme=(!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS']!=='off') ? 'https' : 'http';
            $base=$scheme.'://'.($_SERVER['HTTP_HOST']??'localhost');
        }
        $photoUrl=$base.'/api/public/index.php?route=media%2Fprofile&file='.rawurlencode($filename);

        $pdo=db();
        $pdo->beginTransaction();
        try {
            $pdo->prepare('UPDATE users SET photo_url=? WHERE id=?')->execute([$photoUrl,$u['id']]);
            if (table_exists($pdo,'donors')) {
                if (column_exists($pdo,'donors','user_id')) {
                    $pdo->prepare('UPDATE donors SET photo_url=? WHERE phone=? OR user_id=?')->execute([$photoUrl,$u['phone'],$u['id']]);
                } else {
                    $pdo->prepare('UPDATE donors SET photo_url=? WHERE phone=?')->execute([$photoUrl,$u['phone']]);
                }
            }
            $pdo->commit();
        } catch (Throwable $e) {
            if ($pdo->inTransaction()) $pdo->rollBack();
            @unlink($destination);
            throw $e;
        }

        $q=$pdo->prepare('SELECT * FROM users WHERE id=?');
        $q->execute([$u['id']]);
        json_response([
            'message'=>'প্রোফাইল ছবি আপডেট হয়েছে',
            'photo_url'=>$photoUrl,
            'user'=>public_user($q->fetch()),
        ]);
    }

    if ($method==='PUT' && $path==='/auth/profile') {
        $u=require_user();$d=input_json();
        $pdo=db();
        $name=clean_string($d['name']??$u['name'],160);
        $phone=clean_string($d['phone']??$u['phone'],40);
        $email=strtolower(clean_string($d['email']??($u['email']??''),190));
        $bloodGroup=clean_string($d['blood_group']??($u['blood_group']??''),5);
        $district=clean_string($d['district']??($u['district']??''),100);
        if ($name==='' || $phone==='') json_response(['message'=>'নাম ও মোবাইল প্রয়োজন'],422);
        if ($email!=='' && !filter_var($email,FILTER_VALIDATE_EMAIL)) json_response(['message'=>'সঠিক ইমেইল দিন'],422);
        $check=$pdo->prepare('SELECT id FROM users WHERE id<>? AND (phone=? OR (?<>\'\' AND email=?)) LIMIT 1');
        $check->execute([$u['id'],$phone,$email,$email]);
        if ($check->fetch()) json_response(['message'=>'এই মোবাইল বা ইমেইল অন্য একাউন্টে ব্যবহার হচ্ছে'],409);

        $oldPhone=(string)$u['phone'];
        $photoUrl=clean_string($u['photo_url']??'',1000);
        $pdo->beginTransaction();
        try {
            $pdo->prepare('UPDATE users SET name=?,email=?,phone=?,blood_group=?,district=? WHERE id=?')->execute([
                $name,$email!==''?$email:null,$phone,$bloodGroup?:null,$district?:null,$u['id'],
            ]);
            if (table_exists($pdo,'donors')) {
                if (column_exists($pdo,'donors','user_id')) {
                    $pdo->prepare("UPDATE donors SET name=?,phone=?,blood_group=CASE WHEN ?<>'' THEN ? ELSE blood_group END,photo_url=CASE WHEN ?<>'' THEN ? ELSE photo_url END WHERE phone=? OR user_id=?")
                        ->execute([$name,$phone,$bloodGroup,$bloodGroup,$photoUrl,$photoUrl,$oldPhone,$u['id']]);
                } else {
                    $pdo->prepare("UPDATE donors SET name=?,phone=?,blood_group=CASE WHEN ?<>'' THEN ? ELSE blood_group END,photo_url=CASE WHEN ?<>'' THEN ? ELSE photo_url END WHERE phone=?")
                        ->execute([$name,$phone,$bloodGroup,$bloodGroup,$photoUrl,$photoUrl,$oldPhone]);
                }
            }
            if (table_exists($pdo,'donor_applications')) {
                $pdo->prepare('UPDATE donor_applications SET name=?,phone=? WHERE user_id=?')->execute([$name,$phone,$u['id']]);
            }
            $pdo->commit();
        } catch (Throwable $e) {
            if ($pdo->inTransaction()) $pdo->rollBack();
            throw $e;
        }
        $q=$pdo->prepare('SELECT * FROM users WHERE id=?');$q->execute([$u['id']]);
        json_response(['message'=>'প্রোফাইল আপডেট হয়েছে','user'=>public_user($q->fetch())]);
    }

    if ($method==='PUT' && $path==='/auth/password') {
        $u=require_user();$d=input_json();
        $current=(string)($d['current_password']??'');$new=(string)($d['new_password']??'');
        if (!password_verify($current,$u['password_hash'])) json_response(['message'=>'বর্তমান পাসওয়ার্ড সঠিক নয়'],422);
        if (strlen($new)<6) json_response(['message'=>'নতুন পাসওয়ার্ড কমপক্ষে ৬ অক্ষরের হতে হবে'],422);
        db()->prepare('UPDATE users SET password_hash=?,token=NULL WHERE id=?')->execute([password_hash($new,PASSWORD_DEFAULT),$u['id']]);
        json_response(['message'=>'পাসওয়ার্ড পরিবর্তন হয়েছে। আবার লগইন করুন।']);
    }

    if ($method==='POST' && $path==='/requests') {
        $u=require_user();
        $d=input_json();
        foreach(['patient_problem','blood_group','donation_date','hospital','contact_phone'] as $k) if(empty($d[$k])) json_response(['message'=>"$k is required"],422);
        $s=db()->prepare('INSERT INTO blood_requests(user_id,patient_problem,blood_group,bag_count,hemoglobin,donation_date,donation_time,hospital,district,contact_phone,details) VALUES(?,?,?,?,?,?,?,?,?,?,?)');
        $s->execute([
            $u['id'],clean_string($d['patient_problem']),clean_string($d['blood_group'],5),max(1,(int)($d['bag_count']??1)),
            clean_string($d['hemoglobin']??'',30),$d['donation_date'],$d['donation_time']??null,clean_string($d['hospital']),
            clean_string($d['district']??'',100),clean_string($d['contact_phone'],40),clean_string($d['details']??'',2000),
        ]);
        json_response(['message'=>'Request submitted','id'=>(int)db()->lastInsertId()],201);
    }

    // ---------------- ADMIN ----------------
    if ($method==='POST' && $path==='/admin/login') {
        $d=input_json();$email=strtolower(clean_string($d['email']??'',190));$pass=(string)($d['password']??'');
        $s=db()->prepare('SELECT * FROM admins WHERE email=? LIMIT 1');$s->execute([$email]);$a=$s->fetch();
        if(!$a || !password_verify($pass,$a['password_hash'])) json_response(['message'=>'Invalid credentials'],401);
        $token=bin2hex(random_bytes(32));db()->prepare('UPDATE admins SET token=? WHERE id=?')->execute([$token,$a['id']]);
        json_response(['token'=>$token,'admin'=>['id'=>(int)$a['id'],'name'=>$a['name'],'email'=>$a['email']]]);
    }

    if ($method==='GET' && $path==='/admin/dashboard') {
        require_admin();$pdo=db();
        json_response(['stats'=>[
            'users'=>(int)$pdo->query('SELECT COUNT(*) FROM users')->fetchColumn(),
            'active_users'=>(int)$pdo->query("SELECT COUNT(*) FROM users WHERE status='active'")->fetchColumn(),
            'blocked_users'=>(int)$pdo->query("SELECT COUNT(*) FROM users WHERE status='blocked'")->fetchColumn(),
            'donors'=>(int)$pdo->query('SELECT COUNT(*) FROM donors')->fetchColumn(),
            'requests'=>(int)$pdo->query('SELECT COUNT(*) FROM blood_requests')->fetchColumn(),
            'pending'=>(int)$pdo->query("SELECT COUNT(*) FROM blood_requests WHERE status='pending'")->fetchColumn(),
            'pending_donor_applications'=>(int)$pdo->query("SELECT COUNT(*) FROM donor_applications WHERE status='pending'")->fetchColumn(),
            'campaigns'=>(int)$pdo->query('SELECT COUNT(*) FROM campaigns WHERE is_active=1')->fetchColumn(),
        ]]);
    }

    if ($method==='GET' && $path==='/admin/users') {
        require_admin();
        $where=['1=1'];$args=[];
        if (!empty($_GET['q'])) {
            $q='%'.clean_string($_GET['q'],120).'%';
            $where[]='(name LIKE ? OR phone LIKE ? OR email LIKE ? OR district LIKE ?)';array_push($args,$q,$q,$q,$q);
        }
        if (!empty($_GET['status']) && in_array($_GET['status'],['active','blocked'],true)) {$where[]='status=?';$args[]=$_GET['status'];}
        $s=db()->prepare('SELECT id,name,email,phone,blood_group,district,photo_url,status,verified,created_at,last_login_at FROM users WHERE '.implode(' AND ',$where).' ORDER BY id DESC LIMIT 1000');
        $s->execute($args);json_response(['data'=>$s->fetchAll()]);
    }

    if ($method==='PUT' && preg_match('#^/admin/users/(\d+)$#',$path,$m)) {
        require_admin();$id=(int)$m[1];$d=input_json();
        $s=db()->prepare('SELECT * FROM users WHERE id=?');$s->execute([$id]);$u=$s->fetch();
        if(!$u) json_response(['message'=>'User not found'],404);
        $status=$d['status']??$u['status'];if(!in_array($status,['active','blocked'],true))$status=$u['status'];
        $verified=array_key_exists('verified',$d)?bool_int($d['verified']):(int)$u['verified'];
        $name=clean_string($d['name']??$u['name'],160);$phone=clean_string($d['phone']??$u['phone'],40);
        $email=strtolower(clean_string($d['email']??($u['email']??''),190));
        if ($email!=='' && !filter_var($email,FILTER_VALIDATE_EMAIL)) json_response(['message'=>'Invalid email'],422);
        $dup=db()->prepare('SELECT id FROM users WHERE id<>? AND (phone=? OR (?<>\'\' AND email=?)) LIMIT 1');$dup->execute([$id,$phone,$email,$email]);
        if($dup->fetch()) json_response(['message'=>'Phone/email already used'],409);
        $token=($status==='blocked')?null:$u['token'];
        db()->prepare('UPDATE users SET name=?,email=?,phone=?,blood_group=?,district=?,status=?,verified=?,token=? WHERE id=?')->execute([
            $name,$email!==''?$email:null,$phone,clean_string($d['blood_group']??($u['blood_group']??''),5)?:null,
            clean_string($d['district']??($u['district']??''),100)?:null,$status,$verified,$token,$id,
        ]);
        json_response(['message'=>'User updated']);
    }

    if ($method==='POST' && preg_match('#^/admin/users/(\d+)/reset-password$#',$path,$m)) {
        require_admin();$d=input_json();$new=(string)($d['password']??'');
        if(strlen($new)<6) json_response(['message'=>'Password must be at least 6 characters'],422);
        $stmt=db()->prepare('UPDATE users SET password_hash=?,token=NULL WHERE id=?');$stmt->execute([password_hash($new,PASSWORD_DEFAULT),(int)$m[1]]);
        if($stmt->rowCount()===0) json_response(['message'=>'User not found'],404);
        json_response(['message'=>'Password reset']);
    }

    if ($method==='DELETE' && preg_match('#^/admin/users/(\d+)$#',$path,$m)) {
        require_admin();$id=(int)$m[1];
        db()->prepare('UPDATE blood_requests SET user_id=NULL WHERE user_id=?')->execute([$id]);
        $stmt=db()->prepare('DELETE FROM users WHERE id=?');$stmt->execute([$id]);
        json_response(['message'=>'User deleted']);
    }

    if ($method==='GET' && $path==='/admin/districts') {
        require_admin();json_response(['data'=>db()->query('SELECT id,name,sort_order,is_active FROM districts ORDER BY sort_order,name,id')->fetchAll()]);
    }
    if ($method==='POST' && $path==='/admin/districts') {
        require_admin();$d=input_json();$name=clean_string($d['name']??'',120);if($name==='')json_response(['message'=>'District name is required'],422);
        try{$s=db()->prepare('INSERT INTO districts(name,sort_order,is_active) VALUES(?,?,?)');$s->execute([$name,(int)($d['sort_order']??0),bool_int($d['is_active']??1)]);}catch(PDOException $e){if(($e->errorInfo[1]??0)==1062)json_response(['message'=>'District already exists'],409);throw $e;}
        json_response(['message'=>'District added','id'=>(int)db()->lastInsertId()],201);
    }
    if ($method==='DELETE' && preg_match('#^/admin/districts/(\d+)$#',$path,$m)) {
        require_admin();$id=(int)$m[1];$s=db()->prepare('SELECT name FROM districts WHERE id=?');$s->execute([$id]);$name=$s->fetchColumn();if($name===false)json_response(['message'=>'District not found'],404);
        $c=db()->prepare('SELECT COUNT(*) FROM donors WHERE district=?');$c->execute([$name]);if((int)$c->fetchColumn()>0)json_response(['message'=>'এই district-এ donor আছে, তাই delete করা যাবে না'],409);
        db()->prepare('DELETE FROM thanas WHERE district_id=?')->execute([$id]);
        db()->prepare('DELETE FROM districts WHERE id=?')->execute([$id]);json_response(['message'=>'District deleted']);
    }

    if ($method==='GET' && $path==='/admin/thanas') {
        require_admin();$where='';$args=[];if(!empty($_GET['district_id'])){$where=' WHERE t.district_id=?';$args[]=(int)$_GET['district_id'];}
        $s=db()->prepare('SELECT t.id,t.district_id,t.name,t.sort_order,t.is_active,d.name AS district_name FROM thanas t JOIN districts d ON d.id=t.district_id'.$where.' ORDER BY d.sort_order,t.sort_order,t.name,t.id');$s->execute($args);json_response(['data'=>$s->fetchAll()]);
    }
    if ($method==='POST' && $path==='/admin/thanas') {
        require_admin();$d=input_json();$districtId=(int)($d['district_id']??0);$name=clean_string($d['name']??'',120);if($districtId<=0||$name==='')json_response(['message'=>'District and thana name are required'],422);
        $s=db()->prepare('SELECT id FROM districts WHERE id=?');$s->execute([$districtId]);if(!$s->fetch())json_response(['message'=>'District not found'],404);
        try{$s=db()->prepare('INSERT INTO thanas(district_id,name,sort_order,is_active) VALUES(?,?,?,?)');$s->execute([$districtId,$name,(int)($d['sort_order']??0),bool_int($d['is_active']??1)]);}catch(PDOException $e){if(($e->errorInfo[1]??0)==1062)json_response(['message'=>'Thana already exists in this district'],409);throw $e;}
        json_response(['message'=>'Thana added','id'=>(int)db()->lastInsertId()],201);
    }
    if ($method==='DELETE' && preg_match('#^/admin/thanas/(\d+)$#',$path,$m)) {
        require_admin();$id=(int)$m[1];$s=db()->prepare('SELECT t.name,d.name AS district_name FROM thanas t JOIN districts d ON d.id=t.district_id WHERE t.id=?');$s->execute([$id]);$row=$s->fetch();if(!$row)json_response(['message'=>'Thana not found'],404);
        $c=db()->prepare('SELECT COUNT(*) FROM donors WHERE district=? AND upazila=?');$c->execute([$row['district_name'],$row['name']]);if((int)$c->fetchColumn()>0)json_response(['message'=>'এই thana-তে donor আছে, তাই delete করা যাবে না'],409);
        db()->prepare('DELETE FROM thanas WHERE id=?')->execute([$id]);json_response(['message'=>'Thana deleted']);
    }

    if ($method==='GET' && $path==='/admin/blood-groups') {
        require_admin();json_response(['data'=>db()->query('SELECT id,name,sort_order,is_active FROM blood_groups ORDER BY sort_order,name,id')->fetchAll()]);
    }
    if ($method==='POST' && $path==='/admin/blood-groups') {
        require_admin();$d=input_json();$name=strtoupper(clean_string($d['name']??'',5));if($name==='')json_response(['message'=>'Blood group is required'],422);
        try{$s=db()->prepare('INSERT INTO blood_groups(name,sort_order,is_active) VALUES(?,?,?)');$s->execute([$name,(int)($d['sort_order']??0),bool_int($d['is_active']??1)]);}catch(PDOException $e){if(($e->errorInfo[1]??0)==1062)json_response(['message'=>'Blood group already exists'],409);throw $e;}
        json_response(['message'=>'Blood group added','id'=>(int)db()->lastInsertId()],201);
    }
    if ($method==='DELETE' && preg_match('#^/admin/blood-groups/(\d+)$#',$path,$m)) {
        require_admin();$id=(int)$m[1];$s=db()->prepare('SELECT name FROM blood_groups WHERE id=?');$s->execute([$id]);$name=$s->fetchColumn();if($name===false)json_response(['message'=>'Blood group not found'],404);
        $c=db()->prepare('SELECT COUNT(*) FROM donors WHERE blood_group=?');$c->execute([$name]);if((int)$c->fetchColumn()>0)json_response(['message'=>'এই blood group-এ donor আছে, তাই delete করা যাবে না'],409);
        db()->prepare('DELETE FROM blood_groups WHERE id=?')->execute([$id]);json_response(['message'=>'Blood group deleted']);
    }

    if ($method==='GET' && $path==='/admin/donor-applications') {
        require_admin();$pdo=db();ensure_donor_application_schema($pdo);
        $where=['1=1'];$args=[];
        $status=clean_string($_GET['status']??'',20);
        if(in_array($status,['pending','approved','rejected'],true)){$where[]='da.status=?';$args[]=$status;}
        if(!empty($_GET['q'])){
            $q='%'.clean_string($_GET['q'],120).'%';
            $where[]='(da.name LIKE ? OR da.phone LIKE ? OR da.district LIKE ? OR da.upazila LIKE ? OR da.blood_group LIKE ?)';
            array_push($args,$q,$q,$q,$q,$q);
        }
        $sql="SELECT da.*,u.email,u.photo_url,u.status AS user_status,(SELECT d.id FROM donors d WHERE d.user_id=da.user_id ORDER BY d.id ASC LIMIT 1) AS donor_id FROM donor_applications da JOIN users u ON u.id=da.user_id WHERE ".implode(' AND ',$where)." ORDER BY CASE da.status WHEN 'pending' THEN 0 WHEN 'approved' THEN 1 ELSE 2 END,da.updated_at DESC,da.id DESC LIMIT 1000";
        $q=$pdo->prepare($sql);$q->execute($args);json_response(['data'=>$q->fetchAll()]);
    }

    if ($method==='PUT' && preg_match('#^/admin/donor-applications/(\d+)$#',$path,$m)) {
        require_admin();$pdo=db();ensure_donor_application_schema($pdo);$d=input_json();
        $status=clean_string($d['status']??'',20);
        if(!in_array($status,['approved','rejected'],true))json_response(['message'=>'Status must be approved or rejected'],422);
        $q=$pdo->prepare('SELECT da.*,u.photo_url FROM donor_applications da JOIN users u ON u.id=da.user_id WHERE da.id=? LIMIT 1');
        $q->execute([(int)$m[1]]);$app=$q->fetch();if(!$app)json_response(['message'=>'Donor application not found'],404);
        $note=clean_string($d['admin_note']??'',500);
        $pdo->beginTransaction();
        try{
            $pdo->prepare('UPDATE donor_applications SET status=?,admin_note=?,reviewed_at=NOW() WHERE id=?')->execute([$status,$note!==''?$note:null,(int)$m[1]]);
            $app['status']=$status;
            if($status==='approved'){
                $donorId=sync_approved_donor($pdo,$app);
            }else{
                $pdo->prepare('DELETE FROM donors WHERE user_id=?')->execute([(int)$app['user_id']]);
            }
            $pdo->commit();
        }catch(Throwable $e){if($pdo->inTransaction())$pdo->rollBack();throw $e;}
        json_response(['message'=>$status==='approved'?'Donor approved and added to donor list':'Donor application rejected']);
    }

    if ($method==='GET' && $path==='/admin/donors') {require_admin();json_response(['data'=>db()->query('SELECT * FROM donors ORDER BY id DESC LIMIT 500')->fetchAll()]);}
    if ($method==='POST' && $path==='/admin/donors') {
        require_admin();$d=input_json();
        foreach(['name','phone'] as $k) if(empty($d[$k])) json_response(['message'=>"$k is required"],422);
        $pdo=db();$district='';$thana='';$bloodGroup='';
        if(!empty($d['district_id'])){$district=catalog_name_by_id($pdo,'districts',(int)$d['district_id'])??'';}else{$district=clean_string($d['district']??'',100);}
        if(!empty($d['blood_group_id'])){$bloodGroup=catalog_name_by_id($pdo,'blood_groups',(int)$d['blood_group_id'])??'';}else{$bloodGroup=clean_string($d['blood_group']??'',5);}
        if(!empty($d['thana_id'])){
            $s=$pdo->prepare('SELECT t.name,t.district_id,d.name AS district_name FROM thanas t JOIN districts d ON d.id=t.district_id WHERE t.id=? LIMIT 1');$s->execute([(int)$d['thana_id']]);$t=$s->fetch();
            if(!$t)json_response(['message'=>'Thana not found'],404);
            if(!empty($d['district_id']) && (int)$t['district_id']!==(int)$d['district_id'])json_response(['message'=>'Thana does not belong to selected district'],422);
            $thana=(string)$t['name'];$district=(string)$t['district_name'];
        }else{$thana=clean_string($d['upazila']??($d['thana']??''),100);}
        if($district===''||$thana===''||$bloodGroup==='')json_response(['message'=>'District, thana and blood group are required'],422);
        $phone=clean_string($d['phone'],40);
        $photoUrl=clean_string($d['photo_url']??'',1000);
        if($photoUrl==='' && table_exists($pdo,'users')){
            $uPhoto=$pdo->prepare('SELECT photo_url FROM users WHERE phone=? LIMIT 1');
            $uPhoto->execute([$phone]);
            $photoUrl=clean_string($uPhoto->fetchColumn()?:'',1000);
        }
        $s=$pdo->prepare('INSERT INTO donors(name,phone,blood_group,district,upazila,address,photo_url,last_donation_date,available,verified) VALUES(?,?,?,?,?,?,?,?,?,?)');
        $s->execute([clean_string($d['name'],160),$phone,$bloodGroup,$district,$thana,clean_string($d['address']??''),$photoUrl?:null,$d['last_donation_date']??null,bool_int($d['available']??1),bool_int($d['verified']??0)]);
        json_response(['message'=>'Donor added','id'=>(int)$pdo->lastInsertId()],201);
    }

    if ($method==='GET' && $path==='/admin/requests') {
        require_admin();
        json_response(['data'=>db()->query('SELECT r.*,u.name AS user_name,u.phone AS user_phone FROM blood_requests r LEFT JOIN users u ON u.id=r.user_id ORDER BY r.id DESC LIMIT 500')->fetchAll()]);
    }
    if ($method==='PUT' && preg_match('#^/admin/requests/(\d+)$#',$path,$m)) {
        require_admin();$d=input_json();$allowed=['pending','approved','completed','cancelled'];$status=$d['status']??'';
        if(!in_array($status,$allowed,true))json_response(['message'=>'Invalid status'],422);
        db()->prepare('UPDATE blood_requests SET status=? WHERE id=?')->execute([$status,(int)$m[1]]);json_response(['message'=>'Updated']);
    }

    if ($method==='GET' && $path==='/admin/banners') {
        require_admin();
        json_response(['data'=>db()->query('SELECT * FROM banners ORDER BY sort_order,id DESC')->fetchAll()]);
    }

    if ($method==='POST' && $path==='/admin/banners/upload') {
        require_admin();
        if (!isset($_FILES['banner']) || !is_array($_FILES['banner'])) {
            json_response(['message'=>'Banner image নির্বাচন করুন'],422);
        }
        $file=$_FILES['banner'];
        $uploadError=(int)($file['error']??UPLOAD_ERR_NO_FILE);
        if ($uploadError!==UPLOAD_ERR_OK) {
            $message=in_array($uploadError,[UPLOAD_ERR_INI_SIZE,UPLOAD_ERR_FORM_SIZE],true)
                ? 'Banner image-এর size server limit-এর বেশি'
                : 'Banner image upload করা যায়নি';
            json_response(['message'=>$message],422);
        }
        $size=(int)($file['size']??0);
        if ($size<=0 || $size>8*1024*1024) {
            json_response(['message'=>'Banner image সর্বোচ্চ ৮ MB হতে পারবে'],422);
        }
        $tmp=(string)($file['tmp_name']??'');
        if ($tmp==='' || !is_uploaded_file($tmp)) {
            json_response(['message'=>'সঠিক banner upload file পাওয়া যায়নি'],422);
        }
        $imageInfo=@getimagesize($tmp);
        $mime=is_array($imageInfo) ? (string)($imageInfo['mime']??'') : '';
        $extensions=['image/jpeg'=>'jpg','image/png'=>'png','image/webp'=>'webp'];
        if (!isset($extensions[$mime])) {
            json_response(['message'=>'শুধু JPG, PNG অথবা WEBP banner upload করা যাবে'],422);
        }
        $uploadDir=__DIR__.'/uploads/banners';
        if (!is_dir($uploadDir) && !mkdir($uploadDir,0755,true) && !is_dir($uploadDir)) {
            json_response(['message'=>'Banner upload folder তৈরি করা যায়নি'],500);
        }
        $filename='banner_'.date('Ymd_His').'_'.bin2hex(random_bytes(6)).'.'.$extensions[$mime];
        $destination=$uploadDir.'/'.$filename;
        if (!move_uploaded_file($tmp,$destination)) {
            json_response(['message'=>'Banner server-এ save করা যায়নি'],500);
        }
        json_response(['message'=>'Banner image uploaded','image_url'=>banner_media_url($filename)],201);
    }

    if ($method==='POST' && $path==='/admin/banners') {
        require_admin();$d=input_json();
        $title=clean_string($d['title']??'',190);
        $subtitle=clean_string($d['subtitle']??'',255);
        $imageUrl=clean_string($d['image_url']??'',1500);
        $linkUrl=clean_string($d['link_url']??'',1500);
        if ($imageUrl==='') json_response(['message'=>'Banner image প্রয়োজন'],422);
        if ($linkUrl!=='' && !filter_var($linkUrl,FILTER_VALIDATE_URL)) {
            json_response(['message'=>'Click URL সঠিক নয়'],422);
        }
        $s=db()->prepare('INSERT INTO banners(title,subtitle,image_url,link_url,is_active,sort_order) VALUES(?,?,?,?,?,?)');
        $s->execute([$title,$subtitle,$imageUrl,$linkUrl?:null,bool_int($d['is_active']??1),(int)($d['sort_order']??0)]);
        json_response(['message'=>'Banner added','id'=>(int)db()->lastInsertId()],201);
    }

    if ($method==='PUT' && preg_match('#^/admin/banners/(\d+)$#',$path,$m)) {
        require_admin();$id=(int)$m[1];$d=input_json();
        $q=db()->prepare('SELECT * FROM banners WHERE id=? LIMIT 1');$q->execute([$id]);$b=$q->fetch();
        if(!$b) json_response(['message'=>'Banner not found'],404);
        $title=clean_string($d['title']??$b['title'],190);
        $subtitle=clean_string($d['subtitle']??($b['subtitle']??''),255);
        $imageUrl=clean_string($d['image_url']??($b['image_url']??''),1500);
        $linkUrl=clean_string($d['link_url']??($b['link_url']??''),1500);
        if ($linkUrl!=='' && !filter_var($linkUrl,FILTER_VALIDATE_URL)) json_response(['message'=>'Click URL সঠিক নয়'],422);
        $active=array_key_exists('is_active',$d)?bool_int($d['is_active']):(int)$b['is_active'];
        $sort=array_key_exists('sort_order',$d)?(int)$d['sort_order']:(int)$b['sort_order'];
        db()->prepare('UPDATE banners SET title=?,subtitle=?,image_url=?,link_url=?,is_active=?,sort_order=? WHERE id=?')
            ->execute([$title,$subtitle,$imageUrl,$linkUrl?:null,$active,$sort,$id]);
        json_response(['message'=>'Banner updated']);
    }

    if ($method==='DELETE' && preg_match('#^/admin/banners/(\d+)$#',$path,$m)) {
        require_admin();db()->prepare('DELETE FROM banners WHERE id=?')->execute([(int)$m[1]]);json_response(['message'=>'Deleted']);
    }

    if ($method==='GET' && $path==='/admin/settings') {require_admin();json_response(app_settings(db()));}
    if ($method==='PUT' && $path==='/admin/settings') {
        require_admin();$d=input_json();$current=app_settings(db());
        $values=[clean_string($d['organization_name']??$current['organization_name'],190),clean_string($d['tagline']??$current['tagline']),clean_string($d['primary_color']??$current['primary_color'],20),clean_string($d['emergency_phone']??$current['emergency_phone'],40)];
        $s=db()->prepare('INSERT INTO app_settings(id,organization_name,tagline,primary_color,emergency_phone) VALUES(1,?,?,?,?) ON DUPLICATE KEY UPDATE organization_name=VALUES(organization_name),tagline=VALUES(tagline),primary_color=VALUES(primary_color),emergency_phone=VALUES(emergency_phone)');$s->execute($values);
        json_response(['message'=>'Settings updated','settings'=>app_settings(db())]);
    }

    json_response(['message'=>'Not found','path'=>$path],404);
} catch (Throwable $e) {
    json_response(['message'=>'Server error','error'=>$e->getMessage()],500);
}
