#!/usr/bin/env python3
"""
Test script for hotel verification functionality.

This script tests the hotel verification query implementation
to ensure it works correctly and doesn't break existing functionality.
"""

import unittest
from unittest.mock import Mock, patch, MagicMock
import sys
import os

# Add the project root to the path
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))

from core.oracle import verify_hotel_connection, OracleWrapper, create_oracle_wrapper
from core.logger import get_rateloader_logger, get_hotellabexport_logger
from features.export_mailer import ExportMailer


class TestHotelVerification(unittest.TestCase):
    """Test cases for hotel verification functionality."""

    def setUp(self):
        """Set up test fixtures."""
        self.mock_connection = Mock()
        self.mock_cursor = Mock()
        self.mock_connection.cursor.return_value = self.mock_cursor

    def test_verify_hotel_connection_success(self):
        """Test successful hotel verification."""
        # Mock successful query result
        self.mock_cursor.fetchone.return_value = ["Test Hotel Name"]
        
        with patch('core.oracle.get_rateloader_logger') as mock_logger:
            mock_logger_instance = Mock()
            mock_logger.return_value = mock_logger_instance
            
            result = verify_hotel_connection(self.mock_connection)
            
            # Verify result
            self.assertEqual(result, "Test Hotel Name")
            
            # Verify query was executed
            self.mock_cursor.execute.assert_called_once()
            self.mock_cursor.close.assert_called_once()
            
            # Verify logging
            mock_logger_instance.info.assert_called_with("Hotel verification successful: Test Hotel Name")

    def test_verify_hotel_connection_no_results(self):
        """Test hotel verification with no results."""
        # Mock empty query result
        self.mock_cursor.fetchone.return_value = None
        
        with patch('core.oracle.get_rateloader_logger') as mock_logger:
            mock_logger_instance = Mock()
            mock_logger.return_value = mock_logger_instance
            
            result = verify_hotel_connection(self.mock_connection)
            
            # Verify result
            self.assertIsNone(result)
            
            # Verify warning was logged
            mock_logger_instance.warning.assert_called_with("Hotel verification query returned no results")

    def test_verify_hotel_connection_database_error(self):
        """Test hotel verification with database error."""
        # Mock database error
        import cx_Oracle
        self.mock_cursor.execute.side_effect = cx_Oracle.DatabaseError("ORA-00942: table or view does not exist")
        
        with patch('core.oracle.get_rateloader_logger') as mock_logger:
            mock_logger_instance = Mock()
            mock_logger.return_value = mock_logger_instance
            
            result = verify_hotel_connection(self.mock_connection)
            
            # Verify result
            self.assertIsNone(result)
            
            # Verify error was logged
            mock_logger_instance.error.assert_called()

    def test_oracle_wrapper_get_hotel_name(self):
        """Test OracleWrapper.get_hotel_name method."""
        wrapper = OracleWrapper()
        wrapper.connection = self.mock_connection
        
        # Mock successful verification
        self.mock_cursor.fetchone.return_value = ["Hotel From Wrapper"]
        
        with patch('core.oracle.verify_hotel_connection') as mock_verify:
            mock_verify.return_value = "Hotel From Wrapper"
            
            result = wrapper.get_hotel_name()
            
            # Verify result
            self.assertEqual(result, "Hotel From Wrapper")
            mock_verify.assert_called_once_with(self.mock_connection)

    def test_oracle_wrapper_get_hotel_name_not_connected(self):
        """Test OracleWrapper.get_hotel_name when not connected."""
        wrapper = OracleWrapper()
        wrapper.connection = None
        
        result = wrapper.get_hotel_name()
        
        # Verify result
        self.assertIsNone(result)

    @patch('features.export_mailer.create_oracle_wrapper')
    @patch('features.export_mailer.create_mailer')
    @patch('features.export_mailer.get_config')
    @patch('features.export_mailer.get_hotellabexport_logger')
    def test_export_mailer_hotel_verification(self, mock_logger, mock_config, mock_mailer, mock_oracle):
        """Test ExportMailer includes hotel verification in execute_query_to_csv."""
        # Setup mocks
        mock_logger_instance = Mock()
        mock_logger.return_value = mock_logger_instance
        
        mock_config_instance = Mock()
        mock_config.return_value = mock_config_instance
        mock_config_instance.get.return_value = "cp1251"
        
        mock_wrapper = Mock()
        mock_oracle.return_value = mock_wrapper
        mock_wrapper.is_connected.return_value = True
        mock_wrapper.get_hotel_name.return_value = "Test Hotel"
        mock_wrapper.stream_query.return_value = [["data1"], ["data2"]]
        
        mock_mailer_instance = Mock()
        mock_mailer.return_value = mock_mailer_instance
        
        # Create ExportMailer and test
        export_mailer = ExportMailer()
        
        # Mock file operations
        with patch('builtins.open', unittest.mock.mock_open()):
            result = export_mailer.execute_query_to_csv("SELECT 1 FROM DUAL", "test.csv")
        
        # Verify hotel name was logged
        mock_logger_instance.info.assert_any_call("Connected to database for hotel: Test Hotel")
        
        # Verify stream_query was called
        mock_wrapper.stream_query.assert_called_once()

    @patch('core.oracle.verify_hotel_connection')
    @patch('cx_Oracle.connect')
    @patch('core.oracle.get_rateloader_logger')
    def test_connect_with_hotel_verification(self, mock_logger, mock_cx_connect, mock_verify):
        """Test that connect function includes hotel verification."""
        # Setup mocks
        mock_connection = Mock()
        mock_cx_connect.return_value = mock_connection
        mock_verify.return_value = "Connected Hotel"
        mock_logger_instance = Mock()
        mock_logger.return_value = mock_logger_instance
        
        # Import and test connect function
        from core.oracle import connect
        result = connect("test/string")
        
        # Verify connection was established
        self.assertEqual(result, mock_connection)
        
        # Verify hotel verification was called
        mock_verify.assert_called_once_with(mock_connection)
        
        # Verify hotel name was logged
        mock_logger_instance.info.assert_any_call("Connected to hotel: Connected Hotel")

    def test_backward_compatibility(self):
        """Test that existing functionality still works after hotel verification changes."""
        # Test that OracleWrapper can still be created and used normally
        wrapper = create_oracle_wrapper()
        self.assertIsInstance(wrapper, OracleWrapper)
        
        # Test that ExportMailer can still be created normally
        with patch('features.export_mailer.create_oracle_wrapper'), \
             patch('features.export_mailer.create_mailer'), \
             patch('features.export_mailer.get_config'), \
             patch('features.export_mailer.get_hotellabexport_logger'):
            
            export_mailer = ExportMailer()
            self.assertIsInstance(export_mailer, ExportMailer)


def main():
    """Run all tests."""
    print("=== Hotel Verification Tests ===\n")
    
    # Create test suite
    suite = unittest.TestLoader().loadTestsFromTestCase(TestHotelVerification)
    
    # Run tests
    runner = unittest.TextTestRunner(verbosity=2)
    result = runner.run(suite)
    
    # Print summary
    print(f"\n=== Test Summary ===")
    print(f"Tests run: {result.testsRun}")
    print(f"Failures: {len(result.failures)}")
    print(f"Errors: {len(result.errors)}")
    
    if result.failures:
        print("\nFailures:")
        for test, traceback in result.failures:
            print(f"- {test}: {traceback}")
    
    if result.errors:
        print("\nErrors:")
        for test, traceback in result.errors:
            print(f"- {test}: {traceback}")
    
    success = len(result.failures) == 0 and len(result.errors) == 0
    print(f"\nOverall: {'✓ PASSED' if success else '✗ FAILED'}")
    
    return success


if __name__ == "__main__":
    success = main()
    sys.exit(0 if success else 1)