"""
auth.py 인증 모듈 테스트
테스터: 아르고스
대상 파일: /home/jay/workspace/dashboard/auth.py
"""

import base64
import sys
import unittest
from unittest.mock import patch

sys.path.insert(0, '/home/jay/workspace/dashboard')

from auth import (
    check_cors_origin,
    get_allowed_origins,
    get_auth_config,
    is_auth_required,
    verify_basic_auth,
)


class TestGetAuthConfig(unittest.TestCase):
    """get_auth_config() 함수 테스트"""

    def test_defaults_when_no_env_vars(self):
        """환경변수 미설정 시 기본값 확인"""
        env = {}
        with patch.dict('os.environ', env, clear=True):
            config = get_auth_config()
        self.assertFalse(config['enabled'])
        self.assertEqual(config['user'], 'admin')
        self.assertEqual(config['password'], 'changeme')

    def test_enabled_true_when_dashboard_auth_true(self):
        """DASHBOARD_AUTH=true 설정 시 enabled=True"""
        with patch.dict('os.environ', {'DASHBOARD_AUTH': 'true'}, clear=True):
            config = get_auth_config()
        self.assertTrue(config['enabled'])

    def test_enabled_false_when_dashboard_auth_false(self):
        """DASHBOARD_AUTH=false 설정 시 enabled=False"""
        with patch.dict('os.environ', {'DASHBOARD_AUTH': 'false'}, clear=True):
            config = get_auth_config()
        self.assertFalse(config['enabled'])

    def test_enabled_case_insensitive(self):
        """DASHBOARD_AUTH 값은 .lower() 처리로 대소문자 무관하게 'true' 비교"""
        # auth.py: os.environ.get("DASHBOARD_AUTH", "").lower() == "true"
        # 'TRUE'.lower() == 'true' → enabled=True
        with patch.dict('os.environ', {'DASHBOARD_AUTH': 'TRUE'}, clear=True):
            config = get_auth_config()
        self.assertTrue(config['enabled'])  # 대소문자 무관하게 true로 인정

    def test_dashboard_password_takes_priority_over_dashboard_pass(self):
        """DASHBOARD_PASSWORD > DASHBOARD_PASS 우선순위"""
        env = {
            'DASHBOARD_PASSWORD': 'secret_password',
            'DASHBOARD_PASS': 'fallback_pass',
        }
        with patch.dict('os.environ', env, clear=True):
            config = get_auth_config()
        self.assertEqual(config['password'], 'secret_password')

    def test_dashboard_pass_used_when_password_not_set(self):
        """DASHBOARD_PASSWORD 미설정 시 DASHBOARD_PASS 사용"""
        with patch.dict('os.environ', {'DASHBOARD_PASS': 'fallback_pass'}, clear=True):
            config = get_auth_config()
        self.assertEqual(config['password'], 'fallback_pass')

    def test_dashboard_password_empty_string_falls_back_to_dashboard_pass(self):
        """DASHBOARD_PASSWORD가 빈 문자열이면 DASHBOARD_PASS로 폴백"""
        env = {
            'DASHBOARD_PASSWORD': '',
            'DASHBOARD_PASS': 'fallback_pass',
        }
        with patch.dict('os.environ', env, clear=True):
            config = get_auth_config()
        # 빈 문자열은 falsy이므로 DASHBOARD_PASS로 폴백
        self.assertEqual(config['password'], 'fallback_pass')

    def test_custom_user(self):
        """DASHBOARD_USER 커스텀 설정 확인"""
        with patch.dict('os.environ', {'DASHBOARD_USER': 'myuser'}, clear=True):
            config = get_auth_config()
        self.assertEqual(config['user'], 'myuser')


class TestIsAuthRequired(unittest.TestCase):
    """is_auth_required(path) 함수 테스트"""

    def test_returns_false_when_auth_disabled(self):
        """AUTH 비활성화 시 모든 경로에서 False"""
        with patch.dict('os.environ', {}, clear=True):
            self.assertFalse(is_auth_required('/dashboard'))
            self.assertFalse(is_auth_required('/api/status'))
            self.assertFalse(is_auth_required('/'))

    def test_returns_true_for_normal_path_when_auth_enabled(self):
        """AUTH 활성화 시 일반 경로는 True"""
        with patch.dict('os.environ', {'DASHBOARD_AUTH': 'true'}, clear=True):
            self.assertTrue(is_auth_required('/dashboard'))
            self.assertTrue(is_auth_required('/'))
            self.assertTrue(is_auth_required('/api/data'))

    def test_exempt_path_api_status_returns_false(self):
        """면제 경로 /api/status → AUTH 활성화 시에도 False"""
        with patch.dict('os.environ', {'DASHBOARD_AUTH': 'true'}, clear=True):
            self.assertFalse(is_auth_required('/api/status'))

    def test_exempt_path_api_stream_returns_false(self):
        """면제 경로 /api/stream → AUTH 활성화 시에도 False"""
        with patch.dict('os.environ', {'DASHBOARD_AUTH': 'true'}, clear=True):
            self.assertFalse(is_auth_required('/api/stream'))

    def test_non_exempt_api_path_requires_auth(self):
        """면제 목록에 없는 /api/* 경로는 인증 필요"""
        with patch.dict('os.environ', {'DASHBOARD_AUTH': 'true'}, clear=True):
            self.assertTrue(is_auth_required('/api/metrics'))
            self.assertTrue(is_auth_required('/api/logs'))


class TestVerifyBasicAuth(unittest.TestCase):
    """verify_basic_auth(auth_header) 함수 테스트"""

    def _make_basic_header(self, user: str, password: str) -> str:
        """테스트용 Basic Auth 헤더 생성 헬퍼"""
        credentials = base64.b64encode(f'{user}:{password}'.encode()).decode()
        return f'Basic {credentials}'

    def test_valid_credentials_returns_true_and_username(self):
        """올바른 Basic auth → (True, username)"""
        env = {
            'DASHBOARD_USER': 'admin',
            'DASHBOARD_PASSWORD': 'changeme',
        }
        with patch.dict('os.environ', env, clear=True):
            header = self._make_basic_header('admin', 'changeme')
            success, username = verify_basic_auth(header)
        self.assertTrue(success)
        self.assertEqual(username, 'admin')

    def test_wrong_password_returns_false_none(self):
        """잘못된 password → (False, None)"""
        env = {
            'DASHBOARD_USER': 'admin',
            'DASHBOARD_PASSWORD': 'changeme',
        }
        with patch.dict('os.environ', env, clear=True):
            header = self._make_basic_header('admin', 'wrongpassword')
            success, username = verify_basic_auth(header)
        self.assertFalse(success)
        self.assertIsNone(username)

    def test_wrong_user_returns_false_none(self):
        """잘못된 username → (False, None)"""
        env = {
            'DASHBOARD_USER': 'admin',
            'DASHBOARD_PASSWORD': 'changeme',
        }
        with patch.dict('os.environ', env, clear=True):
            header = self._make_basic_header('wronguser', 'changeme')
            success, username = verify_basic_auth(header)
        self.assertFalse(success)
        self.assertIsNone(username)

    def test_empty_header_returns_false_none(self):
        """빈 헤더 → (False, None)"""
        with patch.dict('os.environ', {}, clear=True):
            success, username = verify_basic_auth('')
        self.assertFalse(success)
        self.assertIsNone(username)

    def test_bearer_token_returns_false_none(self):
        """"Bearer xxx" 형식 → (False, None)"""
        with patch.dict('os.environ', {}, clear=True):
            success, username = verify_basic_auth('Bearer sometoken123')
        self.assertFalse(success)
        self.assertIsNone(username)

    def test_invalid_base64_returns_false_none(self):
        """잘못된 base64 → (False, None)"""
        with patch.dict('os.environ', {}, clear=True):
            success, username = verify_basic_auth('Basic !!!invalid_base64!!!')
        self.assertFalse(success)
        self.assertIsNone(username)

    def test_no_colon_in_decoded_returns_false_none(self):
        """콜론 없는 base64 디코딩 → (False, None)"""
        with patch.dict('os.environ', {}, clear=True):
            no_colon = base64.b64encode(b'nocolon').decode()
            success, username = verify_basic_auth(f'Basic {no_colon}')
        self.assertFalse(success)
        self.assertIsNone(username)

    def test_password_with_colon(self):
        """패스워드에 콜론 포함 시 올바르게 처리"""
        env = {
            'DASHBOARD_USER': 'admin',
            'DASHBOARD_PASSWORD': 'pass:with:colons',
        }
        with patch.dict('os.environ', env, clear=True):
            header = self._make_basic_header('admin', 'pass:with:colons')
            success, username = verify_basic_auth(header)
        self.assertTrue(success)
        self.assertEqual(username, 'admin')


class TestGetAllowedOrigins(unittest.TestCase):
    """get_allowed_origins() 함수 테스트"""

    def test_default_origins(self):
        """기본값 확인"""
        with patch.dict('os.environ', {}, clear=True):
            origins = get_allowed_origins()
        self.assertIn('http://100.76.130.39:8000', origins)
        self.assertIn('http://localhost:8000', origins)
        self.assertEqual(len(origins), 2)

    def test_custom_cors_origins_single(self):
        """CORS_ORIGINS 단일 커스텀 설정"""
        with patch.dict('os.environ', {'CORS_ORIGINS': 'http://example.com'}, clear=True):
            origins = get_allowed_origins()
        self.assertEqual(origins, ['http://example.com'])

    def test_custom_cors_origins_multiple(self):
        """CORS_ORIGINS 다중 커스텀 설정 (콤마 구분)"""
        custom = 'http://example.com,http://another.com,http://third.com'
        with patch.dict('os.environ', {'CORS_ORIGINS': custom}, clear=True):
            origins = get_allowed_origins()
        self.assertEqual(len(origins), 3)
        self.assertIn('http://example.com', origins)
        self.assertIn('http://another.com', origins)
        self.assertIn('http://third.com', origins)

    def test_returns_list_type(self):
        """반환값이 list 타입인지 확인"""
        with patch.dict('os.environ', {}, clear=True):
            origins = get_allowed_origins()
        self.assertIsInstance(origins, list)


class TestCheckCorsOrigin(unittest.TestCase):
    """check_cors_origin(origin) 함수 테스트"""

    def test_allowed_origin_returns_origin(self):
        """허용된 origin → 해당 origin 반환"""
        with patch.dict('os.environ', {}, clear=True):
            result = check_cors_origin('http://localhost:8000')
        self.assertEqual(result, 'http://localhost:8000')

    def test_another_allowed_origin_returns_origin(self):
        """다른 허용된 origin (기본값) → 해당 origin 반환"""
        with patch.dict('os.environ', {}, clear=True):
            result = check_cors_origin('http://100.76.130.39:8000')
        self.assertEqual(result, 'http://100.76.130.39:8000')

    def test_disallowed_origin_returns_none(self):
        """허용되지 않은 origin → None"""
        with patch.dict('os.environ', {}, clear=True):
            result = check_cors_origin('http://evil.com')
        self.assertIsNone(result)

    def test_custom_allowed_origin(self):
        """커스텀 CORS_ORIGINS 환경변수로 허용된 origin"""
        with patch.dict('os.environ', {'CORS_ORIGINS': 'http://myapp.com'}, clear=True):
            result = check_cors_origin('http://myapp.com')
        self.assertEqual(result, 'http://myapp.com')

    def test_custom_disallowed_origin(self):
        """커스텀 CORS_ORIGINS 환경변수에 없는 origin → None"""
        with patch.dict('os.environ', {'CORS_ORIGINS': 'http://myapp.com'}, clear=True):
            result = check_cors_origin('http://localhost:8000')
        self.assertIsNone(result)

    def test_empty_origin_returns_none(self):
        """빈 origin 문자열 → None"""
        with patch.dict('os.environ', {}, clear=True):
            result = check_cors_origin('')
        self.assertIsNone(result)


if __name__ == '__main__':
    unittest.main(verbosity=2)
