<?php

/**
 * Tracking Class for Item Lifecycle Management
 * Handles logging and retrieving item tracking events
 */
class Tracking {
    private $conn;
    private $logger;

    public function __construct($conn, $logger = null) {
        $this->conn = $conn;
        $this->logger = $logger;
    }

    /**
     * Log an item tracking event
     *
     * @param int $itemId Item ID
     * @param string $uniqueCode Item unique code
     * @param string $eventType Event type: 'added', 'shipped', 'received', 'sold'
     * @param int|null $branchId Branch ID (for shipped/received/sold events)
     * @param int|null $shipmentId Shipment ID (for shipped/received events)
     * @param int|null $userId User who performed the action
     * @param array|null $details Additional details as JSON
     * @return bool Success status
     */
    public function logEvent($itemId, $uniqueCode, $eventType, $branchId = null, $shipmentId = null, $userId = null, $details = null) {
        try {
            // Validate that referenced records exist before inserting
            if ($branchId !== null) {
                $checkBranch = $this->conn->prepare("SELECT branch_id FROM branches WHERE branch_id = :branch_id");
                $checkBranch->bindValue(':branch_id', $branchId, PDO::PARAM_INT);
                $checkBranch->execute();
                $branchResult = $checkBranch->fetch();
                if (!$branchResult) {
                    error_log("Tracking: Branch ID $branchId does not exist, setting to NULL");
                    $branchId = null;
                }
            }

            if ($shipmentId !== null) {
                $checkShipment = $this->conn->prepare("SELECT shipment_id FROM shipments WHERE shipment_id = :shipment_id");
                $checkShipment->bindValue(':shipment_id', $shipmentId, PDO::PARAM_INT);
                $checkShipment->execute();
                $shipmentResult = $checkShipment->fetch();
                if (!$shipmentResult) {
                    error_log("Tracking: Shipment ID $shipmentId does not exist, setting to NULL");
                    $shipmentId = null;
                }
            }

            if ($userId !== null) {
                $checkUser = $this->conn->prepare("SELECT user_id FROM users WHERE user_id = :user_id");
                $checkUser->bindValue(':user_id', $userId, PDO::PARAM_INT);
                $checkUser->execute();
                $userResult = $checkUser->fetch();
                if (!$userResult) {
                    error_log("Tracking: User ID $userId does not exist, setting to NULL");
                    $userId = null;
                }
            }

            $sql = "INSERT INTO item_tracking (item_id, unique_code, event_type, branch_id, shipment_id, user_id, details)
                    VALUES (:item_id, :unique_code, :event_type, :branch_id, :shipment_id, :user_id, :details)";

            $stmt = $this->conn->prepare($sql);
            $stmt->bindValue(':item_id', $itemId, PDO::PARAM_INT);
            $stmt->bindValue(':unique_code', $uniqueCode, PDO::PARAM_STR);
            $stmt->bindValue(':event_type', $eventType, PDO::PARAM_STR);
            $stmt->bindValue(':branch_id', $branchId, PDO::PARAM_INT);
            $stmt->bindValue(':shipment_id', $shipmentId, PDO::PARAM_INT);
            $stmt->bindValue(':user_id', $userId, PDO::PARAM_INT);
            $stmt->bindValue(':details', json_encode($details), PDO::PARAM_STR);

            $result = $stmt->execute();
            error_log("Tracking: Insert result for $eventType event on item $uniqueCode: " . ($result ? 'SUCCESS' : 'FAILED'));
            return $result;
        } catch (Exception $e) {
            error_log("Error logging tracking event: " . $e->getMessage());
            return false;
        }
    }

    /**
     * Get item tracking history by unique code
     *
     * @param string $uniqueCode Item unique code
     * @return array Array of tracking events
     */
    public function getItemHistory($uniqueCode) {
        try {
            $sql = "SELECT it.*,
                            b.branch_name,
                            u.username as performed_by,
                            s.sender_name as shipment_sender,
                            fb.branch_name as from_branch_name,
                            tb.branch_name as to_branch_name
                     FROM item_tracking it
                     LEFT JOIN branches b ON it.branch_id = b.branch_id
                     LEFT JOIN users u ON it.user_id = u.user_id
                     LEFT JOIN shipments s ON it.shipment_id = s.shipment_id
                     LEFT JOIN branches fb ON s.from_branch_id = fb.branch_id
                     LEFT JOIN branches tb ON s.to_branch_id = tb.branch_id
                     WHERE it.unique_code = :unique_code
                     ORDER BY it.event_date ASC";

            $stmt = $this->conn->prepare($sql);
            $stmt->bindParam(':unique_code', $uniqueCode, PDO::PARAM_STR);
            $stmt->execute();

            $results = $stmt->fetchAll(PDO::FETCH_ASSOC);
            error_log("Tracking query for $uniqueCode returned " . count($results) . " events");

            // Debug: Log what we found
            foreach ($results as $result) {
                error_log("Found event: " . $result['event_type'] . " for " . $result['unique_code']);
            }

            return $results;
        } catch (Exception $e) {
            error_log("Error getting item history: " . $e->getMessage());
            return [];
        }
    }

    /**
     * Get item tracking history by unique code with role-based access control
     *
     * @param string $uniqueCode Item unique code
     * @param int $userId User ID
     * @param string $userRole User role ('admin', 'warehouse', 'branch_manager')
     * @return array Array of tracking events or error
     */
    public function getItemHistoryWithAccessControl($uniqueCode, $userId, $userRole) {
        try {
            // First check if the item exists and get its current location
            $itemCheckSql = "SELECT i.item_id, i.current_location_id, b.branch_name
                           FROM items i
                           LEFT JOIN branches b ON i.current_location_id = b.branch_id
                           WHERE i.unique_code = :unique_code";

            $itemCheckStmt = $this->conn->prepare($itemCheckSql);
            $itemCheckStmt->bindValue(':unique_code', $uniqueCode, PDO::PARAM_STR);
            $itemCheckStmt->execute();
            $itemInfo = $itemCheckStmt->fetch(PDO::FETCH_ASSOC);

            if (!$itemInfo) {
                return ['error' => 'Item not found'];
            }

            // Check access permissions based on user role
            if ($userRole === 'branch_manager') {
                // Get user's branch
                $userBranchSql = "SELECT branch_id FROM users WHERE user_id = :user_id";
                $userBranchStmt = $this->conn->prepare($userBranchSql);
                $userBranchStmt->bindValue(':user_id', $userId, PDO::PARAM_INT);
                $userBranchStmt->execute();
                $userBranch = $userBranchStmt->fetch(PDO::FETCH_ASSOC);

                if (!$userBranch) {
                    return ['error' => 'User branch not found'];
                }

                // Check if item has been shipped to this branch
                $branchAccessSql = "SELECT COUNT(*) as access_count FROM item_tracking
                                  WHERE unique_code = :unique_code
                                  AND branch_id = :branch_id
                                  AND event_type IN ('received', 'sold')";

                $branchAccessStmt = $this->conn->prepare($branchAccessSql);
                $branchAccessStmt->bindValue(':unique_code', $uniqueCode, PDO::PARAM_STR);
                $branchAccessStmt->bindValue(':branch_id', $userBranch['branch_id'], PDO::PARAM_INT);
                $branchAccessStmt->execute();
                $accessResult = $branchAccessStmt->fetch(PDO::FETCH_ASSOC);

                if ($accessResult['access_count'] == 0) {
                    return ['error' => 'You can only track items that have been shipped to your branch'];
                }
            }

            // If access is granted, get the full history
            return $this->getItemHistory($uniqueCode);

        } catch (Exception $e) {
            error_log("Error getting item history with access control: " . $e->getMessage());
            return ['error' => 'Database error: ' . $e->getMessage()];
        }
    }

    /**
     * Get basic item information and tracking history
     *
     * @param string $uniqueCode Item unique code
     * @return array Array with item info and tracking events
     */
    public function getItemInfoAndHistory($uniqueCode) {
        try {
            // First get basic item information
            $sql = "SELECT i.*,
                            b.branch_name as current_location_name,
                            creator.username as created_by_name
                     FROM items i
                     LEFT JOIN branches b ON i.current_location_id = b.branch_id
                     LEFT JOIN users creator ON i.created_by = creator.user_id
                     WHERE i.unique_code = :unique_code";

            $stmt = $this->conn->prepare($sql);
            $stmt->bindValue(':unique_code', $uniqueCode, PDO::PARAM_STR);
            $stmt->execute();
            $itemInfo = $stmt->fetch(PDO::FETCH_ASSOC);

            if (!$itemInfo) {
                return ['error' => 'Item not found'];
            }

            // Get tracking history
            $trackingHistory = $this->getItemHistory($uniqueCode);

            // If no tracking events exist, create a basic "added" event from item data
            if (empty($trackingHistory)) {
                $basicEvent = [
                    'tracking_id' => null,
                    'item_id' => $itemInfo['item_id'],
                    'unique_code' => $itemInfo['unique_code'],
                    'event_type' => 'added',
                    'event_date' => $itemInfo['created_at'],
                    'branch_id' => $itemInfo['current_location_id'],
                    'branch_name' => $itemInfo['current_location_name'],
                    'shipment_id' => null,
                    'user_id' => $itemInfo['created_by'],
                    'performed_by' => $itemInfo['created_by_name'],
                    'details' => json_encode([
                        'item_name' => $itemInfo['item_name'],
                        'condition_status' => $itemInfo['condition_status'],
                        'unit_price' => $itemInfo['unit_price'],
                        'source' => 'warehouse_inventory'
                    ]),
                    'shipment_sender' => null
                ];
                $trackingHistory = [$basicEvent];
            }

            return [
                'item_info' => $itemInfo,
                'tracking_history' => $trackingHistory
            ];

        } catch (Exception $e) {
            error_log("Error getting item info and history: " . $e->getMessage());
            return ['error' => 'Database error: ' . $e->getMessage()];
        }
    }

    /**
     * Get basic item information and tracking history with role-based access control
     *
     * @param string $uniqueCode Item unique code
     * @param int $userId User ID
     * @param string $userRole User role ('admin', 'warehouse', 'branch_manager')
     * @return array Array with item info and tracking events or error
     */
    public function getItemInfoAndHistoryWithAccessControl($uniqueCode, $userId, $userRole) {
        try {
            // First check access permissions
            $accessCheck = $this->getItemHistoryWithAccessControl($uniqueCode, $userId, $userRole);
            if (isset($accessCheck['error'])) {
                return $accessCheck;
            }

            // If access is granted, get the full item info and history
            return $this->getItemInfoAndHistory($uniqueCode);

        } catch (Exception $e) {
            error_log("Error getting item info and history with access control: " . $e->getMessage());
            return ['error' => 'Database error: ' . $e->getMessage()];
        }
    }

    /**
     * Get item tracking history by item ID
     *
     * @param int $itemId Item ID
     * @return array Array of tracking events
     */
    public function getItemHistoryById($itemId) {
        try {
            $sql = "SELECT it.*,
                           b.branch_name,
                           u.username as performed_by,
                           s.sender_name as shipment_sender
                    FROM item_tracking it
                    LEFT JOIN branches b ON it.branch_id = b.branch_id
                    LEFT JOIN users u ON it.user_id = u.user_id
                    LEFT JOIN shipments s ON it.shipment_id = s.shipment_id
                    WHERE it.item_id = :item_id
                    ORDER BY it.event_date ASC";

            $stmt = $this->conn->prepare($sql);
            $stmt->bindParam(':item_id', $itemId, PDO::PARAM_INT);
            $stmt->execute();

            return $stmt->fetchAll(PDO::FETCH_ASSOC);
        } catch (Exception $e) {
            error_log("Error getting item history by ID: " . $e->getMessage());
            return [];
        }
    }

    /**
     * Get recent tracking events
     *
     * @param int $limit Number of events to return
     * @return array Array of recent tracking events
     */
    public function getRecentEvents($limit = 50) {
        try {
            $sql = "SELECT it.*,
                           i.item_name,
                           i.item_details,
                           b.branch_name,
                           u.username as performed_by
                    FROM item_tracking it
                    JOIN items i ON it.item_id = i.item_id
                    LEFT JOIN branches b ON it.branch_id = b.branch_id
                    LEFT JOIN users u ON it.user_id = u.user_id
                    ORDER BY it.event_date DESC
                    LIMIT :limit";

            $stmt = $this->conn->prepare($sql);
            $stmt->bindParam(':limit', $limit, PDO::PARAM_INT);
            $stmt->execute();

            return $stmt->fetchAll(PDO::FETCH_ASSOC);
        } catch (Exception $e) {
            error_log("Error getting recent events: " . $e->getMessage());
            return [];
        }
    }

    /**
     * Get tracking events by branch
     *
     * @param int $branchId Branch ID
     * @param string $eventType Optional event type filter
     * @return array Array of tracking events
     */
    public function getEventsByBranch($branchId, $eventType = null) {
        try {
            $sql = "SELECT it.*,
                           i.item_name,
                           i.item_details,
                           u.username as performed_by
                    FROM item_tracking it
                    JOIN items i ON it.item_id = i.item_id
                    LEFT JOIN users u ON it.user_id = u.user_id
                    WHERE it.branch_id = :branch_id";

            if ($eventType) {
                $sql .= " AND it.event_type = :event_type";
            }

            $sql .= " ORDER BY it.event_date DESC";

            $stmt = $this->conn->prepare($sql);
            $stmt->bindParam(':branch_id', $branchId, PDO::PARAM_INT);

            if ($eventType) {
                $stmt->bindParam(':event_type', $eventType, PDO::PARAM_STR);
            }

            $stmt->execute();
            return $stmt->fetchAll(PDO::FETCH_ASSOC);
        } catch (Exception $e) {
            error_log("Error getting events by branch: " . $e->getMessage());
            return [];
        }
    }

    public function getTransactionInfoWithAccessControl($transactionId, $userId, $userRole) {
        try {
            $sql = "SELECT si.sale_id,
                           si.transaction_id,
                           si.branch_id,
                           si.unique_code,
                           si.unit_price,
                           si.total_price,
                           si.sold_at,
                           si.customer_name,
                           si.customer_phone,
                           i.item_name,
                           i.item_details,
                           i.description,
                           b.branch_name,
                           u.username as sold_by_name
                    FROM sold_items si
                    LEFT JOIN items i ON si.item_id = i.item_id
                    LEFT JOIN branches b ON si.branch_id = b.branch_id
                    LEFT JOIN users u ON si.sold_by = u.user_id
                    WHERE (si.transaction_id = :transaction_id
                       OR si.transaction_id LIKE :verified_transaction_id ESCAPE '\\\\')";

            if ($userRole === 'branch_manager') {
                $sql .= " AND si.branch_id = (
                            SELECT branch_id
                            FROM users
                            WHERE user_id = :user_id
                            LIMIT 1
                          )";
            }

            $sql .= " ORDER BY si.sale_id ASC";

            $verifiedTransactionPattern = addcslashes($transactionId, '\\%_') . '\\_VERIFIED\\_%';

            $stmt = $this->conn->prepare($sql);
            $stmt->bindValue(':transaction_id', $transactionId, PDO::PARAM_STR);
            $stmt->bindValue(':verified_transaction_id', $verifiedTransactionPattern, PDO::PARAM_STR);
            if ($userRole === 'branch_manager') {
                $stmt->bindValue(':user_id', $userId, PDO::PARAM_INT);
            }
            $stmt->execute();

            $items = $stmt->fetchAll(PDO::FETCH_ASSOC);
            if (empty($items)) {
                return ['error' => 'Transaction not found or you do not have permission to view it.'];
            }

            $firstItem = $items[0];
            $totalAmount = 0;
            foreach ($items as $item) {
                $totalAmount += (float)($item['total_price'] ?? 0);
            }

            return [
                'transaction_info' => [
                    'transaction_id' => $firstItem['transaction_id'],
                    'branch_id' => $firstItem['branch_id'],
                    'branch_name' => $firstItem['branch_name'],
                    'sold_at' => $firstItem['sold_at'],
                    'sold_by_name' => $firstItem['sold_by_name'],
                    'customer_name' => $firstItem['customer_name'],
                    'customer_phone' => $firstItem['customer_phone'],
                    'item_count' => count($items),
                    'total_amount' => $totalAmount
                ],
                'items' => $items
            ];
        } catch (Exception $e) {
            error_log("Error getting transaction info with access control: " . $e->getMessage());
            return ['error' => 'Database error: ' . $e->getMessage()];
        }
    }

    /**
     * Escape special LIKE characters for use with ESCAPE '\\'
     */
    private function escapeForLike(string $value): string {
        return str_replace(['\\', '%', '_'], ['\\\\', '\\%', '\\_'], $value);
    }

    /**
     * Find item codes that look most similar to the missing code.
     *
     * @param string $uniqueCode The code that was not found
     * @param int $limit Max suggestions to return
     * @return array Each element has 'unique_code' and 'item_name'
     */
    public function findClosestItemCodes(string $uniqueCode, int $limit = 5): array {
        try {
            $uniqueCode = trim($uniqueCode);
            if ($uniqueCode === '') {
                return [];
            }

            $inputUpper = strtoupper($uniqueCode);
            $inputNormalized = preg_replace('/[^A-Z0-9]/', '', $inputUpper);
            $inputLength = strlen($inputUpper);
            $normalizedLength = strlen($inputNormalized);
            $prefix = substr($inputUpper, 0, min(2, max(1, $inputLength)));
            $normalizedPrefix = substr($inputNormalized, 0, min(2, max(1, $normalizedLength)));
            $minLength = max(1, $inputLength - 4);
            $maxLength = $inputLength + 4;

            $sql = "SELECT unique_code, item_name
                    FROM items
                    WHERE unique_code IS NOT NULL
                      AND unique_code <> ''
                      AND (
                            UPPER(unique_code) LIKE :prefix_pattern
                            OR REPLACE(REPLACE(REPLACE(REPLACE(UPPER(unique_code), '/', ''), '_', ''), '-', ''), ' ', '') LIKE :normalized_prefix_pattern
                            OR CHAR_LENGTH(unique_code) BETWEEN :min_length AND :max_length
                          )
                    ORDER BY CASE WHEN UPPER(unique_code) LIKE :prefix_pattern THEN 0 ELSE 1 END,
                             ABS(CHAR_LENGTH(unique_code) - :input_length),
                             unique_code
                    LIMIT 1000";

            $stmt = $this->conn->prepare($sql);
            $stmt->bindValue(':prefix_pattern', $this->escapeForLike($prefix) . '%', PDO::PARAM_STR);
            $stmt->bindValue(':normalized_prefix_pattern', $this->escapeForLike($normalizedPrefix) . '%', PDO::PARAM_STR);
            $stmt->bindValue(':min_length', $minLength, PDO::PARAM_INT);
            $stmt->bindValue(':max_length', $maxLength, PDO::PARAM_INT);
            $stmt->bindValue(':input_length', $inputLength, PDO::PARAM_INT);
            $stmt->execute();

            $ranked = [];
            foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
                $candidateCode = (string)$row['unique_code'];
                $candidateUpper = strtoupper($candidateCode);
                $candidateNormalized = preg_replace('/[^A-Z0-9]/', '', $candidateUpper);

                $rawDistance = levenshtein($inputUpper, $candidateUpper);
                $normalizedDistance = levenshtein($inputNormalized, $candidateNormalized);
                $score = min($rawDistance, $normalizedDistance);

                if ($normalizedPrefix !== '' && strpos($candidateNormalized, $normalizedPrefix) === 0) {
                    $score -= 0.5;
                }

                $score += abs(strlen($candidateNormalized) - $normalizedLength) * 0.25;

                $ranked[] = [
                    'unique_code' => $candidateCode,
                    'item_name' => $row['item_name'],
                    '_score' => $score,
                ];
            }

            usort($ranked, function ($a, $b) {
                if ($a['_score'] == $b['_score']) {
                    return strcmp($a['unique_code'], $b['unique_code']);
                }
                return $a['_score'] <=> $b['_score'];
            });

            $suggestions = [];
            foreach (array_slice($ranked, 0, $limit) as $row) {
                unset($row['_score']);
                $suggestions[] = $row;
            }

            return $suggestions;
        } catch (Exception $e) {
            error_log("Error finding closest item codes: " . $e->getMessage());
            return [];
        }
    }
    /**
     * Get tracking statistics
     *
     * @return array Statistics about tracking events
     */
    public function getTrackingStats() {
        try {
            $stats = [];

            // Total events
            $sql = "SELECT COUNT(*) as total_events FROM item_tracking";
            $stmt = $this->conn->prepare($sql);
            $stmt->execute();
            $stats['total_events'] = $stmt->fetch(PDO::FETCH_ASSOC)['total_events'];

            // Events by type
            $sql = "SELECT event_type, COUNT(*) as count FROM item_tracking GROUP BY event_type";
            $stmt = $this->conn->prepare($sql);
            $stmt->execute();
            $stats['events_by_type'] = $stmt->fetchAll(PDO::FETCH_ASSOC);

            // Recent activity (last 7 days)
            $sql = "SELECT COUNT(*) as recent_events FROM item_tracking WHERE event_date >= DATE_SUB(NOW(), INTERVAL 7 DAY)";
            $stmt = $this->conn->prepare($sql);
            $stmt->execute();
            $stats['recent_events'] = $stmt->fetch(PDO::FETCH_ASSOC)['recent_events'];

            return $stats;
        } catch (Exception $e) {
            error_log("Error getting tracking stats: " . $e->getMessage());
            return [];
        }
    }
}
?>