<?php
/**
 * download_user_details.php
 * Robust universal photo fetch + PDF generator
 *
 * Supports DB photo values that are:
 *  - simple filename stored in Uploads/
 *  - relative path like "employee/john.jpg"
 *  - absolute filesystem path
 *  - URL (http/https)
 *  - data URI (data:image/...;base64,...)
 *  - binary/blob (the column contains binary image bytes)
 *
 * Strategy:
 * 1) Locate or fetch raw image bytes (from file, URL, datauri, or DB blob)
 * 2) Validate image type via getimagesizefromstring()
 * 3) Convert to a guaranteed JPEG temp file using GD (preferred) or Imagick
 * 4) Insert JPEG into FPDF->Image
 * 5) Cleanup
 *
 * Usage: download_user_details.php?id=EMPID&debug=1
 */

ob_start();

/* ---------- DEBUG ---------- */
$debugParam = (isset($_GET['debug']) && $_GET['debug'] == '1');
$host = $_SERVER['HTTP_HOST'] ?? '';
$DEBUG = $debugParam || (strpos($host, 'localhost') !== false) || (php_sapi_name() === 'cli');
if ($DEBUG) { ini_set('display_errors', 1); ini_set('display_startup_errors', 1); }
error_reporting(E_ALL);

function dbg($msg) {
    global $DEBUG;
    error_log($msg);
    if ($DEBUG) echo "<pre style='color:blue'>" . htmlspecialchars($msg) . "</pre>";
}
function fatal($msg, $http = 500) {
    dbg("FATAL: $msg");
    while (ob_get_level()) ob_end_clean();
    if (!headers_sent()) http_response_code($http);
    echo ($http === 500 ? "Server Error: " : "") . htmlspecialchars($msg);
    exit;
}

/* ---------- CONFIG ---------- */
define('FPDF_PATH', __DIR__ . DIRECTORY_SEPARATOR . 'fpdf.php');
define('DEFAULT_PHOTO', __DIR__ . DIRECTORY_SEPARATOR . 'default-photo.jpg');
define('UPLOAD_DIR', rtrim(__DIR__, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . 'Uploads' . DIRECTORY_SEPARATOR);
$configPath = __DIR__ . DIRECTORY_SEPARATOR . 'config.php';

/* ---------- REQUIREMENTS ---------- */
if (!file_exists(FPDF_PATH)) fatal("Missing FPDF at: " . FPDF_PATH, 500);
require_once FPDF_PATH;

if (!file_exists($configPath)) fatal("Missing config.php at: " . $configPath, 500);
require_once $configPath; // must set $conn (mysqli)

if (!isset($conn) || !($conn instanceof mysqli)) fatal("\$conn is not a mysqli instance. Check config.php", 500);

/* Ensure Uploads exists (try create) */
if (!is_dir(UPLOAD_DIR)) @mkdir(UPLOAD_DIR, 0755, true);
$uploadReal = realpath(UPLOAD_DIR);

/* ---------- INPUT ---------- */
$user_id = trim((string)($_GET['id'] ?? ''));
if ($user_id === '') fatal("Missing ID parameter (use ?id=EMPID)", 400);
if (!preg_match('/^[A-Za-z0-9_\-]+$/', $user_id)) fatal("Invalid ID format.", 400);

/* ---------- FETCH USER ---------- */
$cols = [
    'id','first_name','last_name','email','role','reporting_manager','is_superadmin','status','resignation_date','rejoin_date',
    'basic_salary','responsibilities','role_id','mobileno','address','pancard','aadharcard','photo',
    'designation','division','marital_status','anniversary_date','gender','date_of_birth','date_of_joining','father_name','spouse_name','qualification','previous_experience',
    'company_name','last_company_1','last_company_1_years','last_company_1_salary',
    'total_experience_years','previous_salary','uan_no','bloodgroup','esic_no','bank_name','account_number',
    'ifsc_code','level','performance','position','appointment_type','location','responsibilities_accepted','details_accepted'
];
$colsSql = implode(', ', $cols);

$stmt = $conn->prepare("SELECT $colsSql FROM users WHERE id = ?");
if (!$stmt) fatal("DB prepare failed: " . $conn->error, 500);
if (!$stmt->bind_param('s', $user_id)) { $stmt->close(); fatal("DB bind_param failed: " . $stmt->error, 500); }
if (!$stmt->execute()) { $err = $stmt->error ?: $conn->error; $stmt->close(); fatal("DB execute failed: " . $err, 500); }

$user = false;
if (method_exists($stmt, 'get_result')) {
    $res = $stmt->get_result();
    if ($res) $user = $res->fetch_assoc();
} else {
    $meta = $stmt->result_metadata();
    if ($meta !== false) {
        $fields = []; $refs = [];
        foreach ($meta->fetch_fields() as $f) { $fields[] = $f->name; $refs[] = null; }
        if (!empty($fields)) {
            $bindParams = [];
            foreach ($refs as $i => $v) $bindParams[] = &$refs[$i];
            $stmt->bind_result(...$bindParams);
            if ($stmt->fetch()) {
                $user = [];
                foreach ($fields as $i => $name) $user[$name] = $refs[$i];
            }
        }
        $meta->free();
    } else {
        $stmt->close();
        fatal("DB cannot return results (no get_result and no metadata).", 500);
    }
}
$stmt->close();
if (!$user) fatal("User not found: $user_id", 404);

/* ---------- IMAGE UTILS ---------- */

function is_data_uri($s) {
    return is_string($s) && strpos($s, 'data:') === 0;
}
function is_url($s) {
    return is_string($s) && (strpos($s, 'http://') === 0 || strpos($s, 'https://') === 0);
}
function is_likely_blob($s) {
    // Heuristic: long string with non-printable bytes (binary)
    if (!is_string($s)) return false;
    if (strlen($s) > 2000) return true; // may be base64 or binary
    // check for NULL bytes or high-bit bytes
    return (strpos($s, "\0") !== false) || preg_match('/[^\x09\x0A\x0D\x20-\x7E]/', $s);
}

/**
 * Attempt to get raw image bytes from multiple possible sources:
 * - If $photoField is empty -> false
 * - If data URI -> decode
 * - If URL -> fetch via curl or file_get_contents
 * - If path (absolute/relative) -> read file
 * - If Uploads basename -> read that file
 * - If raw blob in DB -> return blob
 *
 * Returns: array('bytes'=>..., 'source'=>'...', 'mime'=>null-or-mime) or false
 */
function fetch_image_bytes($photoField) {
    dbg("fetch_image_bytes: " . substr((string)$photoField, 0, 200)); // truncated

    if ($photoField === null || $photoField === '') {
        dbg("photoField empty");
        return false;
    }

    $photoField = trim((string)$photoField);

    // If data URI
    if (is_data_uri($photoField)) {
        dbg("Detected data URI");
        if (preg_match('/^data:(image\/[a-zA-Z0-9.+-]+);base64,(.*)$/s', $photoField, $m)) {
            $mime = $m[1];
            $data = base64_decode($m[2]);
            if ($data === false) { dbg("data URI base64 decode failed"); return false; }
            return ['bytes'=>$data, 'source'=>'datauri', 'mime'=>$mime];
        } else {
            dbg("data URI not matching expected pattern");
            return false;
        }
    }

    // If it's a URL
    if (is_url($photoField)) {
        dbg("Detected URL: $photoField");
        $data = null;
        // try curl first for robust fetching
        if (function_exists('curl_init')) {
            $ch = curl_init($photoField);
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
            curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
            curl_setopt($ch, CURLOPT_TIMEOUT, 10);
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
            $data = @curl_exec($ch);
            $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
            if ($data === false) dbg("curl_exec failed: " . curl_error($ch));
            curl_close($ch);
            if ($http_code >= 400) dbg("HTTP code: $http_code");
        }
        if ($data === null || $data === false) {
            // fallback to file_get_contents if allowed
            if (ini_get('allow_url_fopen')) {
                $data = @file_get_contents($photoField);
                if ($data === false) dbg("file_get_contents URL read failed");
            } else {
                dbg("allow_url_fopen disabled and curl failed");
            }
        }
        if ($data) return ['bytes'=>$data, 'source'=>'url', 'mime'=>null];
        return false;
    }

    // If this looks like an absolute or relative path on disk
    $candidates = [];

    // 1) direct path as provided (relative or absolute)
    $candidates[] = $photoField;

    // 2) try path relative to project root
    $candidates[] = __DIR__ . DIRECTORY_SEPARATOR . ltrim($photoField, '/\\');

    // 3) try Uploads basename
    $candidates[] = UPLOAD_DIR . basename($photoField);

    // 4) try Uploads plus the value as-is (if stored "Uploads/john.jpg")
    $candidates[] = __DIR__ . DIRECTORY_SEPARATOR . ltrim($photoField, '/\\');

    foreach ($candidates as $cand) {
        if (!$cand) continue;
        $cand = str_replace(['//','\\\\'], DIRECTORY_SEPARATOR, $cand);
        dbg("Candidate file path: $cand");
        if (file_exists($cand) && is_file($cand) && is_readable($cand)) {
            dbg("Reading file bytes from: $cand");
            $bytes = @file_get_contents($cand);
            if ($bytes !== false) {
                // try to detect mime using getimagesizefromstring
                $info = @getimagesizefromstring($bytes);
                $mime = $info['mime'] ?? null;
                return ['bytes'=>$bytes, 'source'=>$cand, 'mime'=>$mime];
            } else {
                dbg("file_get_contents failed for $cand");
            }
        } else {
            dbg("Candidate not found/readable: $cand");
        }
    }

    // Finally: maybe DB column actually contains binary blob (raw bytes) or base64 string
    if (is_likely_blob($photoField)) {
        dbg("Heuristic: treating DB value as raw blob/base64 candidate");
        // if it looks base64 (long & only base64 chars)
        if (preg_match('#^[A-Za-z0-9+/=\s]+$#', $photoField) && strlen($photoField) > 1000) {
            // remove whitespace and try decode
            $clean = preg_replace('/\s+/', '', $photoField);
            $decoded = base64_decode($clean, true);
            if ($decoded !== false) {
                $info = @getimagesizefromstring($decoded);
                $mime = $info['mime'] ?? null;
                if ($info) return ['bytes'=>$decoded, 'source'=>'db_base64', 'mime'=>$mime];
            }
        }
        // else treat raw bytes directly
        $info = @getimagesizefromstring($photoField);
        if ($info) return ['bytes'=>$photoField, 'source'=>'db_blob', 'mime'=>$info['mime'] ?? null];
    }

    dbg("No source produced image bytes");
    return false;
}

/**
 * Convert raw image bytes to a guaranteed JPEG temp file.
 * Prefer GD (imagecreatefromstring + imagejpeg). If GD not available, try Imagick.
 * Returns path to temp JPG or false.
 */
function convert_to_temp_jpeg($bytes, $suggestedMime = null) {
    dbg("convert_to_temp_jpeg: bytes length=" . strlen($bytes) . " mime=" . ($suggestedMime ?? 'null'));

    // attempt GD
    if (function_exists('imagecreatefromstring') && function_exists('imagejpeg')) {
        dbg("Using GD for conversion");
        $im = @imagecreatefromstring($bytes);
        if ($im !== false) {
            $w = imagesx($im);
            $h = imagesy($im);
            // create truecolor and copy to remove transparency
            $dst = @imagecreatetruecolor($w, $h);
            if ($dst === false) {
                imagedestroy($im);
                dbg("imagecreatetruecolor failed");
                // continue to imagick
            } else {
                $white = imagecolorallocate($dst, 255,255,255);
                imagefill($dst, 0, 0, $white);
                imagecopy($dst, $im, 0, 0, 0, 0, $w, $h);
                imagedestroy($im);
                $tmpBase = tempnam(sys_get_temp_dir(), 'empimg_');
                if ($tmpBase === false) {
                    imagedestroy($dst);
                    dbg("tempnam failed");
                } else {
                    $tmpJpg = $tmpBase . '.jpg';
                    $saved = @imagejpeg($dst, $tmpJpg, 90);
                    imagedestroy($dst);
                    if ($saved && file_exists($tmpJpg)) {
                        @chmod($tmpJpg, 0644);
                        dbg("GD saved JPEG: $tmpJpg");
                        // cleanup old temp without ext
                        if (file_exists($tmpBase)) @unlink($tmpBase);
                        return $tmpJpg;
                    } else {
                        dbg("imagejpeg failed to write");
                        if (file_exists($tmpJpg)) @unlink($tmpJpg);
                    }
                }
            }
        } else {
            dbg("imagecreatefromstring failed (GD)");
        }
    } else {
        dbg("GD not available");
    }

    // attempt Imagick
    if (class_exists('Imagick')) {
        dbg("Using Imagick for conversion");
        try {
            $im = new Imagick();
            $im->readImageBlob($bytes);
            // flatten to remove transparency onto white
            $im = $im->coalesceImages();
            foreach ($im as $frame) {
                $frame->setImageBackgroundColor('white');
                $frame = $frame->mergeImageLayers(Imagick::LAYERMETHOD_FLATTEN);
            }
            // write to temp jpg
            $tmpBase = tempnam(sys_get_temp_dir(), 'empimg_');
            if ($tmpBase === false) { dbg("tempnam failed for imagick"); return false; }
            $tmpJpg = $tmpBase . '.jpg';
            $im->setImageFormat('jpeg');
            $im->setImageCompressionQuality(90);
            $im->writeImage($tmpJpg);
            $im->clear();
            $im->destroy();
            if (file_exists($tmpJpg)) {
                @chmod($tmpJpg, 0644);
                if (file_exists($tmpBase)) @unlink($tmpBase);
                dbg("Imagick wrote JPEG: $tmpJpg");
                return $tmpJpg;
            } else {
                dbg("Imagick failed to write $tmpJpg");
            }
        } catch (Exception $e) {
            dbg("Imagick exception: " . $e->getMessage());
        }
    } else {
        dbg("Imagick not available");
    }

    // As last attempt: write bytes to a temp file and hope FPDF can read it as-is (if jpeg/png)
    $info = @getimagesizefromstring($bytes);
    if ($info && isset($info['mime']) && in_array($info['mime'], ['image/jpeg','image/png'])) {
        dbg("Writing raw image to temp with original extension: mime=" . $info['mime']);
        $ext = stripos($info['mime'],'png') !== false ? 'png' : 'jpg';
        $tmpBase = tempnam(sys_get_temp_dir(), 'empimg_');
        if ($tmpBase !== false) {
            $tmpFile = $tmpBase . '.' . $ext;
            if (@file_put_contents($tmpFile, $bytes) !== false) {
                @chmod($tmpFile, 0644);
                if (file_exists($tmpBase)) @unlink($tmpBase);
                dbg("Wrote fallback temp image: $tmpFile");
                return $tmpFile;
            } else {
                dbg("Failed to write fallback temp image");
                if (file_exists($tmpFile)) @unlink($tmpFile);
            }
        } else {
            dbg("tempnam failed for fallback write");
        }
    } else {
        dbg("getimagesizefromstring did not detect jpeg/png in bytes");
    }

    dbg("All conversion attempts failed");
    return false;
}

/* ---------- PREPARE IMAGE: orchestrator ---------- */
function prepare_user_photo_temp($photoField) {
    // returns path to a temp JPEG (or PNG) usable by FPDF, or false
    dbg("prepare_user_photo_temp START");
    $fetched = fetch_image_bytes($photoField);
    if ($fetched === false) {
        dbg("fetch_image_bytes returned false");
        return false;
    }
    $bytes = $fetched['bytes'] ?? null;
    $mime = $fetched['mime'] ?? null;
    dbg("Fetched source={$fetched['source']} mime=" . ($mime ?? 'null') . " bytes=" . strlen($bytes));
    if (!$bytes) { dbg("No bytes obtained"); return false; }

    // convert to temp jpeg (preferred)
    $tmp = convert_to_temp_jpeg($bytes, $mime);
    if ($tmp !== false) {
        dbg("Conversion produced temp file: $tmp");
        return $tmp;
    }

    dbg("Conversion failed; returning false");
    return false;
}

/* ---------- TRY PREPARE USER PHOTO ---------- */
$userPhotoField = $user['photo'] ?? '';
dbg("User photo field (raw): " . substr((string)$userPhotoField,0,300));

$tmpPhoto = false;
if ($userPhotoField !== null && $userPhotoField !== '') {
    $tmpPhoto = prepare_user_photo_temp($userPhotoField);
}

/* If not prepared, try default-photo.jpg (and convert it similarly) */
if (!$tmpPhoto) {
    dbg("Attempting default-photo fallback");
    if (file_exists(DEFAULT_PHOTO) && is_readable(DEFAULT_PHOTO)) {
        // read default file bytes and convert
        $bytes = @file_get_contents(DEFAULT_PHOTO);
        if ($bytes !== false) {
            $tmpPhoto = convert_to_temp_jpeg($bytes, null);
            if ($tmpPhoto) dbg("Default photo converted: $tmpPhoto");
            else dbg("Default photo conversion failed");
        } else dbg("Default photo exists but reading failed");
    } else {
        dbg("Default-photo not present at " . DEFAULT_PHOTO);
    }
}

/* ---------- PDF CLASS & BUILD ---------- */
class PDF_UDD extends FPDF {
    function Header() {
        $this->SetFont('Arial','B',16);
        $this->Cell(0,12,'Employee Details',0,1,'C');
        $this->Ln(6);
    }
    function Footer() {
        $this->SetY(-15); $this->SetFont('Arial','I',8);
        $this->Cell(0,10,'Page '.$this->PageNo().' – '.date('d-m-Y'),0,0,'C');
    }
    function Section($txt) {
        $this->SetFont('Arial','B',12); $this->SetFillColor(240,240,240);
        $this->Cell(0,9,' '.$txt,0,1,'L',true); $this->Ln(2);
    }
    function Field($label,$value) {
        $this->SetFont('Arial','',10);
        $this->Cell(55,7,$label.':',0,0);
        $value = ($value === null || $value === '') ? 'N/A' : (string)$value;
        $value = str_replace(["\r","\n"], [' ',' '], $value);
        if (strlen($value) > 80) { $this->MultiCell(0,7,$value); $this->Ln(1); }
        else { $this->Cell(0,7,$value,0,1); }
    }
}

$pdf = new PDF_UDD();
$pdf->AddPage();
$pdf->SetAutoPageBreak(true, 20);

/* Photo box coordinates/sizing */
$photoX = 150; $photoY = 30; $photoW = 40;
if ($tmpPhoto && file_exists($tmpPhoto) && is_readable($tmpPhoto)) {
    try {
        dbg("Inserting image into PDF: $tmpPhoto");
        $pdf->Image($tmpPhoto, $photoX, $photoY, $photoW, $photoW);
    } catch (Exception $e) {
        dbg("FPDF Image exception: " . $e->getMessage());
        $pdf->SetXY($photoX, $photoY);
        $pdf->SetFont('Arial','B',10);
        $pdf->Cell($photoW, $photoW, 'NO IMAGE', 1, 0, 'C');
    }
} else {
    dbg("No temp photo available — drawing NO IMAGE box");
    $pdf->SetXY($photoX, $photoY);
    $pdf->SetFont('Arial','B',10);
    $pdf->Cell($photoW, $photoW, 'NO IMAGE', 1, 0, 'C');
}

$pdf->SetXY(10, 30);

/* Fill fields */
$pdf->Section('Personal Information');
$pdf->Field('ID', $user['id'] ?? '');
$pdf->Field('First Name', $user['first_name'] ?? '');
$pdf->Field('Last Name', $user['last_name'] ?? '');
$pdf->Field('Gender', $user['gender'] ?? '');
$pdf->Field('Date of Birth', $user['date_of_birth'] ?? '');
$pdf->Field('Father Name', $user['father_name'] ?? '');
$pdf->Field('Marital Status', $user['marital_status'] ?? '');
$pdf->Field('Spouse Name', $user['spouse_name'] ?? '');
$pdf->Field('Anniversary', $user['anniversary_date'] ?? '');

$pdf->Ln(4);
$pdf->Section('Contact Information');
$pdf->Field('Email', $user['email'] ?? '');
$pdf->Field('Mobile', $user['mobileno'] ?? '');
$pdf->Field('Address', $user['address'] ?? '');
$pdf->Field('PAN', $user['pancard'] ?? '');
$pdf->Field('Aadhar', $user['aadharcard'] ?? '');
$pdf->Field('Blood Group', $user['bloodgroup'] ?? '');

$pdf->Ln(4);
$pdf->Section('Employment Details');
$pdf->Field('Division', $user['division'] ?? '');
$pdf->Field('Designation', $user['designation'] ?? '');
$pdf->Field('Level', $user['level'] ?? '');
$pdf->Field('Position', $user['position'] ?? '');
$pdf->Field('Appointment', $user['appointment_type'] ?? '');
$pdf->Field('Location', $user['location'] ?? '');
$pdf->Field('Joining Date', $user['date_of_joining'] ?? '');
$pdf->Field('Resignation', $user['resignation_date'] ?? '');
$pdf->Field('Status', $user['status'] ?? '');
$pdf->Field('Reporting Mgr', $user['reporting_manager'] ?? '');
$pdf->Field('Responsibilities', $user['responsibilities'] ?? '');
$pdf->Field('Role ID', $user['role_id'] ?? '');

$pdf->Ln(4);
$pdf->Section('Financial Details');
$pdf->Field('Basic Salary', $user['basic_salary'] ?? '');
$pdf->Field('Prev Salary', $user['previous_salary'] ?? '');
$pdf->Field('Bank', $user['bank_name'] ?? '');
$pdf->Field('A/c No', $user['account_number'] ?? '');
$pdf->Field('IFSC', $user['ifsc_code'] ?? '');
$pdf->Field('UAN', $user['uan_no'] ?? '');
$pdf->Field('ESIC', $user['esic_no'] ?? '');

$pdf->Ln(4);
$pdf->Section('Experience Details');
$pdf->Field('Qualification', $user['qualification'] ?? '');
$pdf->Field('Prev Exp', $user['previous_experience'] ?? '');
$pdf->Field('Total Yrs', $user['total_experience_years'] ?? '');
$pdf->Field('Company', $user['company_name'] ?? '');
$pdf->Field('Last Co 1', $user['last_company_1'] ?? '');
$pdf->Field('Yrs 1', $user['last_company_1_years'] ?? '');
$pdf->Field('Sal 1', $user['last_company_1_salary'] ?? '');

$pdf->Ln(4);
$pdf->Section('Additional');
$pdf->Field('Performance', $user['performance'] ?? '');

/* ---------- OUTPUT ---------- */
@ob_end_clean();
$filenameSafe = 'user_details_' . preg_replace('/[^A-Za-z0-9_\-]/', '_', $user_id) . '.pdf';
header('Content-Type: application/pdf');
header('Content-Disposition: attachment; filename="' . $filenameSafe . '"');
header('Cache-Control: private, max-age=0, must-revalidate');
header('Pragma: public');

try {
    $pdf->Output('D', $filenameSafe);
} catch (Exception $e) {
    dbg("PDF output error: " . $e->getMessage());
    fatal("Failed to generate PDF. See server logs.", 500);
} finally {
    // cleanup temp photo if created
    if (!empty($tmpPhoto) && strpos($tmpPhoto, sys_get_temp_dir()) === 0 && file_exists($tmpPhoto)) {
        @unlink($tmpPhoto);
        dbg("Cleaned up temp photo: $tmpPhoto");
    }
}

exit;
