-- database/enhancement.sql
-- ============================================
-- ENHANCEMENT FOR LOST & FOUND DATABASE
-- Stored Procedures, Functions, Triggers, Views
-- ============================================
DELIMITER $$

-- ============================================
-- FUNCTIONS
-- ============================================

-- Function: Hitung similarity score antara 2 string
CREATE FUNCTION fn_calculate_similarity(str1 TEXT, str2 TEXT)
RETURNS DECIMAL(5,2)
DETERMINISTIC
BEGIN
    DECLARE similarity DECIMAL(5,2);
    DECLARE len1 INT;
    DECLARE len2 INT;
    DECLARE common_words INT DEFAULT 0;
    
    SET len1 = LENGTH(str1);
    SET len2 = LENGTH(str2);
    
    -- Simple similarity calculation (bisa diganti dengan algorithm lebih kompleks)
    IF str1 = str2 THEN
        SET similarity = 100.00;
    ELSEIF LOCATE(LOWER(str1), LOWER(str2)) > 0 THEN
        SET similarity = 75.00;
    ELSE
        SET similarity = 50.00;
    END IF;
    
    RETURN similarity;
END$$

-- Function: Check apakah item sudah expired
CREATE FUNCTION fn_is_item_expired(item_id INT)
RETURNS BOOLEAN
READS SQL DATA
BEGIN
    DECLARE is_expired BOOLEAN DEFAULT FALSE;
    DECLARE expire_date TIMESTAMP;
    
    SELECT expires_at INTO expire_date
    FROM items
    WHERE id = item_id AND deleted_at IS NULL;
    
    IF expire_date IS NOT NULL AND expire_date < NOW() THEN
        SET is_expired = TRUE;
    END IF;
    
    RETURN is_expired;
END$$

-- Function: Hitung total items per kategori
CREATE FUNCTION fn_count_items_by_category(cat_id INT)
RETURNS INT
READS SQL DATA
BEGIN
    DECLARE total INT DEFAULT 0;
    
    SELECT COUNT(*) INTO total
    FROM items
    WHERE category_id = cat_id 
    AND deleted_at IS NULL
    AND status = 'unclaimed';
    
    RETURN total;
END$$

-- ============================================
-- STORED PROCEDURES
-- ============================================

-- Procedure: Create claim dengan transaction dan locking
CREATE PROCEDURE sp_create_claim(
    IN p_item_id INT,
    IN p_user_id INT,
    IN p_description TEXT,
    IN p_proof_url VARCHAR(255),
    IN p_contact VARCHAR(50),
    OUT p_claim_id INT,
    OUT p_status VARCHAR(50),
    OUT p_message TEXT
)
BEGIN
    DECLARE v_item_status VARCHAR(50);
    DECLARE v_item_desc TEXT;
    DECLARE v_similarity DECIMAL(5,2);
    DECLARE EXIT HANDLER FOR SQLEXCEPTION
    BEGIN
        ROLLBACK;
        SET p_status = 'error';
        SET p_message = 'Transaction failed. Claim not created.';
        SET p_claim_id = NULL;
    END;
    
    START TRANSACTION;
    
    -- Lock item untuk prevent concurrent claims
    SELECT status, description INTO v_item_status, v_item_desc
    FROM items
    WHERE id = p_item_id AND deleted_at IS NULL
    FOR UPDATE;
    
    -- Validasi: item harus unclaimed
    IF v_item_status != 'unclaimed' THEN
        SET p_status = 'error';
        SET p_message = 'Item sudah diklaim atau tidak tersedia';
        ROLLBACK;
    ELSE
        -- Insert claim
        INSERT INTO claims (
            item_id, user_id, description, 
            proof_url, contact, status
        ) VALUES (
            p_item_id, p_user_id, p_description,
            p_proof_url, p_contact, 'pending'
        );
        
        SET p_claim_id = LAST_INSERT_ID();
        
        -- Calculate similarity score
        SET v_similarity = fn_calculate_similarity(p_description, v_item_desc);
        
        -- Insert verification record
        INSERT INTO claim_verifications (
            claim_id, similarity_score, is_auto_matched
        ) VALUES (
            p_claim_id, v_similarity, FALSE
        );
        
        -- Create notification for managers
        INSERT INTO notifications (
            user_id, type, title, message, entity_type, entity_id
        )
        SELECT 
            u.id,
            'new_claim',
            'Klaim Baru',
            CONCAT('Ada klaim baru untuk barang: ', i.name),
            'claim',
            p_claim_id
        FROM users u
        CROSS JOIN items i
        WHERE u.role_id = 2 
        AND u.deleted_at IS NULL
        AND i.id = p_item_id;
        
        SET p_status = 'success';
        SET p_message = 'Claim created successfully';
        
        COMMIT;
    END IF;
END$$

-- Procedure: Verify claim (approve/reject)
CREATE PROCEDURE sp_verify_claim(
    IN p_claim_id INT,
    IN p_verifier_id INT,
    IN p_action VARCHAR(20), -- 'approve' or 'reject'
    IN p_notes TEXT,
    OUT p_status VARCHAR(50),
    OUT p_message TEXT
)
BEGIN
    DECLARE v_item_id INT;
    DECLARE v_user_id INT;
    DECLARE v_claim_status VARCHAR(50);
    
    DECLARE EXIT HANDLER FOR SQLEXCEPTION
    BEGIN
        ROLLBACK;
        SET p_status = 'error';
        SET p_message = 'Verification failed';
    END;
    
    START TRANSACTION;
    
    -- Get claim details with lock
    SELECT item_id, user_id, status 
    INTO v_item_id, v_user_id, v_claim_status
    FROM claims
    WHERE id = p_claim_id AND deleted_at IS NULL
    FOR UPDATE;
    
    IF v_claim_status != 'pending' THEN
        SET p_status = 'error';
        SET p_message = 'Claim already processed';
        ROLLBACK;
    ELSE
        -- Update claim status
        UPDATE claims
        SET status = p_action,
            notes = p_notes,
            verified_at = NOW(),
            verified_by = p_verifier_id
        WHERE id = p_claim_id;
        
        -- If approved, update item status
        IF p_action = 'approved' THEN
            UPDATE items
            SET status = 'claimed'
            WHERE id = v_item_id;
            
            -- Archive the item
            INSERT INTO archives (
                item_id, name, category_id, photo_url, location,
                description, date_found, status, reporter_name,
                reporter_contact, archived_reason, claimed_by
            )
            SELECT 
                id, name, category_id, photo_url, location,
                description, date_found, status, reporter_name,
                reporter_contact, 'case_closed', v_user_id
            FROM items
            WHERE id = v_item_id;
        END IF;
        
        -- Notify user
        INSERT INTO notifications (
            user_id, type, title, message, entity_type, entity_id
        ) VALUES (
            v_user_id,
            CONCAT('claim_', p_action),
            IF(p_action = 'approved', 'Klaim Disetujui!', 'Klaim Ditolak'),
            CONCAT('Klaim Anda telah ', IF(p_action = 'approved', 'disetujui', 'ditolak')),
            'claim',
            p_claim_id
        );
        
        SET p_status = 'success';
        SET p_message = CONCAT('Claim ', p_action, ' successfully');
        
        COMMIT;
    END IF;
END$$

-- Procedure: Archive expired items
CREATE PROCEDURE sp_archive_expired_items(
    OUT p_archived_count INT
)
BEGIN
    DECLARE done INT DEFAULT FALSE;
    DECLARE v_item_id INT;
    DECLARE cur CURSOR FOR 
        SELECT id FROM items 
        WHERE expires_at < NOW() 
        AND status = 'unclaimed'
        AND deleted_at IS NULL;
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
    
    SET p_archived_count = 0;
    
    OPEN cur;
    
    read_loop: LOOP
        FETCH cur INTO v_item_id;
        IF done THEN
            LEAVE read_loop;
        END IF;
        
        -- Archive the item
        INSERT INTO archives (
            item_id, name, category_id, photo_url, location,
            description, date_found, status, reporter_name,
            reporter_contact, archived_reason
        )
        SELECT 
            id, name, category_id, photo_url, location,
            description, date_found, status, reporter_name,
            reporter_contact, 'expired'
        FROM items
        WHERE id = v_item_id;
        
        -- Update item status
        UPDATE items
        SET status = 'expired'
        WHERE id = v_item_id;
        
        SET p_archived_count = p_archived_count + 1;
    END LOOP;
    
    CLOSE cur;
END$$

-- Procedure: Auto-match lost items with found items
CREATE PROCEDURE sp_auto_match_items()
BEGIN
    DECLARE done INT DEFAULT FALSE;
    DECLARE v_lost_id INT;
    DECLARE v_item_id INT;
    DECLARE v_similarity DECIMAL(5,2);
    
    DECLARE cur CURSOR FOR
        SELECT li.id, i.id, fn_calculate_similarity(li.description, i.description)
        FROM lost_items li
        CROSS JOIN items i
        WHERE li.status = 'active'
        AND i.status = 'unclaimed'
        AND li.category_id = i.category_id
        AND li.deleted_at IS NULL
        AND i.deleted_at IS NULL
        AND NOT EXISTS (
            SELECT 1 FROM match_results mr
            WHERE mr.lost_item_id = li.id
            AND mr.item_id = i.id
            AND mr.deleted_at IS NULL
        );
    
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
    
    OPEN cur;
    
    match_loop: LOOP
        FETCH cur INTO v_lost_id, v_item_id, v_similarity;
        IF done THEN
            LEAVE match_loop;
        END IF;
        
        -- Only create match if similarity >= 70%
        IF v_similarity >= 70.00 THEN
            INSERT INTO match_results (
                lost_item_id, item_id, similarity_score,
                matched_fields, is_notified
            ) VALUES (
                v_lost_id, v_item_id, v_similarity,
                '{"category": 100, "description": ' || v_similarity || '}',
                FALSE
            );
            
            -- Create notification
            INSERT INTO notifications (
                user_id, type, title, message, entity_type, entity_id
            )
            SELECT 
                li.user_id,
                'match_found',
                'Barang yang Mirip Ditemukan!',
                CONCAT('Kami menemukan barang yang mirip: ', i.name),
                'match',
                LAST_INSERT_ID()
            FROM lost_items li
            JOIN items i ON i.id = v_item_id
            WHERE li.id = v_lost_id;
        END IF;
    END LOOP;
    
    CLOSE cur;
END$$

-- ============================================
-- TRIGGERS
-- ============================================

-- Trigger: Auto-set expires_at saat item dibuat (3 bulan dari date_found)
CREATE TRIGGER trg_items_before_insert
BEFORE INSERT ON items
FOR EACH ROW
BEGIN
    IF NEW.expires_at IS NULL THEN
        SET NEW.expires_at = DATE_ADD(NEW.date_found, INTERVAL 3 MONTH);
    END IF;
END$$

-- Trigger: Auto-log ke audit_logs saat user login
CREATE TRIGGER trg_audit_log_after_user_update
AFTER UPDATE ON users
FOR EACH ROW
BEGIN
    IF OLD.updated_at != NEW.updated_at THEN
        INSERT INTO audit_logs (
            user_id, action, entity_type, entity_id, details
        ) VALUES (
            NEW.id,
            'update',
            'user',
            NEW.id,
            CONCAT('User updated: ', NEW.name)
        );
    END IF;
END$$

-- Trigger: Auto-create revision log saat item di-update
CREATE TRIGGER trg_items_after_update
AFTER UPDATE ON items
FOR EACH ROW
BEGIN
    IF OLD.name != NEW.name THEN
        INSERT INTO revision_logs (item_id, user_id, field_name, old_value, new_value)
        VALUES (NEW.id, NEW.reporter_id, 'name', OLD.name, NEW.name);
    END IF;
    
    IF OLD.description != NEW.description THEN
        INSERT INTO revision_logs (item_id, user_id, field_name, old_value, new_value)
        VALUES (NEW.id, NEW.reporter_id, 'description', OLD.description, NEW.description);
    END IF;
    
    IF OLD.status != NEW.status THEN
        INSERT INTO revision_logs (item_id, user_id, field_name, old_value, new_value)
        VALUES (NEW.id, NEW.reporter_id, 'status', OLD.status, NEW.status);
    END IF;
END$$

-- Trigger: Prevent delete if item has active claims
CREATE TRIGGER trg_items_before_delete
BEFORE DELETE ON items
FOR EACH ROW
BEGIN
    DECLARE v_active_claims INT;
    
    SELECT COUNT(*) INTO v_active_claims
    FROM claims
    WHERE item_id = OLD.id
    AND status = 'pending'
    AND deleted_at IS NULL;
    
    IF v_active_claims > 0 THEN
        SIGNAL SQLSTATE '45000'
        SET MESSAGE_TEXT = 'Cannot delete item with active claims';
    END IF;
END$$

-- Trigger: Auto-archive item when claimed
CREATE TRIGGER trg_items_after_status_claimed
AFTER UPDATE ON items
FOR EACH ROW
BEGIN
    IF OLD.status != 'claimed' AND NEW.status = 'claimed' THEN
        INSERT INTO archives (
            item_id, name, category_id, photo_url, location,
            description, date_found, status, reporter_name,
            reporter_contact, archived_reason
        ) VALUES (
            NEW.id, NEW.name, NEW.category_id, NEW.photo_url,
            NEW.location, NEW.description, NEW.date_found,
            NEW.status, NEW.reporter_name, NEW.reporter_contact,
            'case_closed'
        );
    END IF;
END$$

DELIMITER ;

-- ============================================
-- VIEWS untuk ETL dan Reporting
-- ============================================

-- View: Dashboard statistics
CREATE OR REPLACE VIEW vw_dashboard_stats AS
SELECT 
    (SELECT COUNT(*) FROM items WHERE status = 'unclaimed' AND deleted_at IS NULL) AS total_unclaimed,
    (SELECT COUNT(*) FROM items WHERE status = 'claimed' AND deleted_at IS NULL) AS total_claimed,
    (SELECT COUNT(*) FROM lost_items WHERE status = 'active' AND deleted_at IS NULL) AS total_lost_reports,
    (SELECT COUNT(*) FROM claims WHERE status = 'pending' AND deleted_at IS NULL) AS pending_claims,
    (SELECT COUNT(*) FROM match_results WHERE is_notified = FALSE AND deleted_at IS NULL) AS unnotified_matches;

-- View: Items dengan informasi kategori dan reporter
CREATE OR REPLACE VIEW vw_items_detail AS
SELECT 
    i.id,
    i.name,
    c.name AS category_name,
    c.slug AS category_slug,
    i.photo_url,
    i.location,
    i.date_found,
    i.status,
    i.reporter_name,
    i.reporter_contact,
    i.expires_at,
    u.name AS reporter_user_name,
    u.email AS reporter_email,
    DATEDIFF(i.expires_at, NOW()) AS days_until_expire,
    i.created_at
FROM items i
JOIN categories c ON i.category_id = c.id
JOIN users u ON i.reporter_id = u.id
WHERE i.deleted_at IS NULL;

-- View: Claims dengan detail lengkap
CREATE OR REPLACE VIEW vw_claims_detail AS
SELECT 
    c.id,
    c.status,
    i.name AS item_name,
    cat.name AS category_name,
    u.name AS claimant_name,
    u.email AS claimant_email,
    u.phone AS claimant_phone,
    c.description AS claim_description,
    c.contact,
    cv.similarity_score,
    c.verified_at,
    v.name AS verified_by_name,
    c.notes,
    c.created_at
FROM claims c
JOIN items i ON c.item_id = i.id
JOIN categories cat ON i.category_id = cat.id
JOIN users u ON c.user_id = u.id
LEFT JOIN claim_verifications cv ON c.id = cv.claim_id
LEFT JOIN users v ON c.verified_by = v.id
WHERE c.deleted_at IS NULL;

-- View: Match results dengan detail
CREATE OR REPLACE VIEW vw_match_results_detail AS
SELECT 
    mr.id,
    li.name AS lost_item_name,
    li.user_id AS lost_by_user_id,
    u.name AS lost_by_user_name,
    u.email AS lost_by_email,
    i.name AS found_item_name,
    i.reporter_name AS found_by_name,
    mr.similarity_score,
    mr.is_notified,
    mr.matched_at,
    i.id AS found_item_id,
    li.id AS lost_item_id
FROM match_results mr
JOIN lost_items li ON mr.lost_item_id = li.id
JOIN items i ON mr.item_id = i.id
JOIN users u ON li.user_id = u.id
WHERE mr.deleted_at IS NULL
ORDER BY mr.similarity_score DESC;

-- View: Category statistics
CREATE OR REPLACE VIEW vw_category_stats AS
SELECT 
    c.id,
    c.name,
    c.slug,
    COUNT(DISTINCT i.id) AS total_items,
    COUNT(DISTINCT CASE WHEN i.status = 'unclaimed' THEN i.id END) AS unclaimed_items,
    COUNT(DISTINCT CASE WHEN i.status = 'claimed' THEN i.id END) AS claimed_items,
    COUNT(DISTINCT li.id) AS total_lost_reports
FROM categories c
LEFT JOIN items i ON c.id = i.category_id AND i.deleted_at IS NULL
LEFT JOIN lost_items li ON c.id = li.category_id AND li.deleted_at IS NULL
WHERE c.deleted_at IS NULL
GROUP BY c.id, c.name, c.slug;

-- ============================================
-- EVENT SCHEDULER (untuk auto-archiving)
-- ============================================

-- Enable event scheduler
SET GLOBAL event_scheduler = ON;

-- Event: Archive expired items setiap hari jam 1 pagi
CREATE EVENT IF NOT EXISTS evt_archive_expired_items
ON SCHEDULE EVERY 1 DAY
STARTS CURRENT_TIMESTAMP + INTERVAL 1 DAY
DO
    CALL sp_archive_expired_items(@count);

-- Event: Auto-match items setiap 1 jam
CREATE EVENT IF NOT EXISTS evt_auto_match_items
ON SCHEDULE EVERY 1 HOUR
DO
    CALL sp_auto_match_items();

-- ============================================
-- SUCCESS MESSAGE
-- ============================================
SELECT '✅ Database enhancements created!' AS Status;
SELECT '📝 Functions: 3' AS Info;
SELECT '⚙️  Procedures: 4' AS Info;
SELECT '🔔 Triggers: 5' AS Info;
SELECT '📊 Views: 5' AS Info;
SELECT '⏰ Events: 2' AS Info;