"""
열차 정보 분석 및 입출고 추적 유틸리티

이 모듈은 열차번호 분석, 종별/방향 판별, 주박지 입출고 추적 등
열차 운행 관련 모든 분석 기능을 제공합니다. 시각표 데이터와 연동하여
열차의 최종 입고지 및 익일 출고 정보를 예측합니다.

주요 기능:
    - 열차 종별 판별: 영업/회송/시운전/단기/임시
    - 방향 판별: 상선(다대포행) / 하선(노포행)
    - 호선별 제원: 최대 편성 수, 최대 호차 수
    - 입고 추적: 일반 기지(신평/노포) 및 주박지(다대포)
    - 익일 예측: 주박 열차의 다음날 출고 및 입고 정보
    - 비고 생성: HWP 보고서용 입고 정보 텍스트

열차번호 규칙:
    - 끝자리 홀수: 상선 (다대포행)
    - 끝자리 짝수: 하선 (노포행)
    - 끝자리 1,2: 영업 열차
    - 끝자리 3,4: 회송 열차
    - 끝자리 5,6: 시운전 열차
    - 끝자리 7,8: 단기 열차
    - 끝자리 9: 임시 열차

주박지 로직 (다대포):
    입고 순서: [2번선, 3번선, 4번선, 상선]
    출고 순서: [4번선, 상선, 3번선, 2번선]
    
    평일/토요일/휴일별로 다른 열차 번호 매핑:
    - 평일 입고: 1335, 1339, 1349, 1351
    - 토요일 입고: 1315, 1319, 1329, 1331
    - 휴일 입고: 1301, 1305, 1315, 1317

리팩토링 배경:
    ReportService의 열차 분석 및 입출고 추적 로직을 분리.
    복잡한 주박지 로직을 독립적인 클래스로 캡슐화하여
    유지보수성과 테스트 용이성을 향상시켰습니다.

사용 예시:
    >>> from utils.train_analyzer import TrainAnalyzer
    >>> from services.timetable_service import TimetableService
    >>> 
    >>> # 열차 종별/방향 판별
    >>> details = TrainAnalyzer.classify_train_details("1215")
    >>> print(details)
    {'num': '1215', 'type': '영업', 'direction': '다대포행(상선)'}
    >>> 
    >>> # 입고 추적 (TimetableService 필요)
    >>> timetable = TimetableService()
    >>> analyzer = TrainAnalyzer(timetable)
    >>> from datetime import date
    >>> remarks = analyzer.get_schedule_remarks(
    ...     train_details={'num': '1215'},
    ...     diagram_type='weekday',
    ...     current_date=date(2026, 2, 17)
    ... )
    >>> print(remarks)
    신평
    정기
    입고
    예정
    (2/17)
    #1351
    23:45경

의존성:
    - TimetableService: 시각표 조회
    - DateScheduleUtils: 날짜 타입 판별
    - datetime: 날짜 계산

작성자: KH.Choi
최종 수정: 2026-02-17
버전: 2.0 (ReportService에서 분리)
"""
import re
from datetime import timedelta
from utils.date_utils import DateScheduleUtils


class TrainAnalyzer:
    """
    열차 정보 분석 및 입출고 추적
    
    이 클래스는 열차번호 기반 정보 분석과 시각표 데이터를 활용한
    입출고 추적 기능을 제공합니다. 특히 다대포 주박지의 복잡한
    입출고 로직을 체계적으로 처리합니다.
    
    Attributes:
        STABLING_MAP (dict): 주박지 입출고 열차 번호 매핑
        LINE_SPECS (dict): 호선별 제원 정보
        timetable (TimetableService): 시각표 서비스 인스턴스
        logger: 로거 인스턴스
    
    주요 메서드:
        classify_train_details: 열차번호 → 종별/방향 판별
        get_line_spec: 호선 → 제원 정보 반환
        get_schedule_remarks: 입고 정보 비고 텍스트 생성
        _predict_next_day_depot: 익일 입고 예측 (주박 전용)
    
    입고 추적 알고리즘:
        1. 열차번호로 시각표에서 다이아 ID 조회
        2. 다이아 ID로 최종 입고 정보 조회
        3. 입고지가 다대포인 경우:
           a. 주박 리스트에 있으면 익일 추적
           b. 입고 순서 → 출고 순서 매핑
           c. 익일 열차의 최종 입고지 재조회
        4. 입고지가 신평/노포인 경우:
           해당 기지 입고 정보 반환
    
    주박지 매핑 로직:
        입고 인덱스 → 출고 인덱스:
        0 (2번선) → 3 (2번선)
        1 (3번선) → 2 (3번선)
        2 (4번선) → 0 (4번선)
        3 (상선) → 1 (상선)
    
    사용 시나리오:
        1. 보고서 생성 시 비고란에 입고 정보 표시
        2. 열차 종별/방향 자동 판별
        3. 주박 열차의 익일 운행 계획 확인
        4. 호선별 제원 정보 조회
    
    예시:
        >>> analyzer = TrainAnalyzer(timetable_service, logger)
        >>> 
        >>> # 정적 메서드 (인스턴스 불필요)
        >>> TrainAnalyzer.get_line_spec("1호선")
        {'max_set': 51, 'max_car': 8}
        >>> 
        >>> # 인스턴스 메서드 (시각표 필요)
        >>> train_details = {'num': '1335', 'type': '영업', 'direction': '다대포행(상선)'}
        >>> remarks = analyzer.get_schedule_remarks(
        ...     train_details,
        ...     diagram_type='weekday',
        ...     current_date=date(2026, 2, 17)
        ... )
    """
    
    # 주박지(다대포) 입출고 로직 데이터
    STABLING_MAP = {
        "in": {
            "weekday": [1335, 1339, 1349, 1351],
            "saturday": [1315, 1319, 1329, 1331],
            "holiday": [1301, 1305, 1315, 1317]
        },
        "out": {
            "weekday": [2002, 2004, 2022, 2036],
            "saturday": [2002, 2004, 2022, 2038],
            "holiday": [2002, 2004, 2022, 2038]
        }
    }
    
    # 호선별 제원 정보
    LINE_SPECS = {
        "1호선": {"max_set": 51, "max_car": 8},
        "2호선": {"max_set": 56, "max_car": 6},
        "3호선": {"max_set": 20, "max_car": 4},
        "4호선": {"max_set": 17, "max_car": 6},
        "default": {"max_set": 56, "max_car": 8}
    }
    
    def __init__(self, timetable_service=None, logger=None):
        """
        Args:
            timetable_service: TimetableService 인스턴스
            logger: 로거 인스턴스
        """
        self.timetable = timetable_service
        self.logger = logger
    
    @staticmethod
    def get_clean_number(text):
        """문자열에서 숫자만 추출"""
        return re.sub(r'[^0-9]', '', text) if text else ""
    
    @classmethod
    def get_line_spec(cls, line_str):
        """
        호선별 제원 정보 반환
        
        Args:
            line_str (str): "1호선" 등
            
        Returns:
            dict: {"max_set": 51, "max_car": 8}
        """
        if not line_str:
            return cls.LINE_SPECS["default"]
        
        for key, spec in cls.LINE_SPECS.items():
            if key in line_str:
                return spec
        return cls.LINE_SPECS["default"]
    
    @classmethod
    def classify_train_details(cls, train_str, context_dir=""):
        """
        열차번호 기반 종별/방향 판별
        
        Args:
            train_str (str): 열차번호 문자열
            context_dir (str): 문맥에서 유추된 방향
            
        Returns:
            dict: {'num': 열차번호, 'type': 종별, 'direction': 방향}
        """
        train_num = cls.get_clean_number(train_str)
        
        if not train_num:
            return {"num": "", "type": "미확인", "direction": context_dir or "미확인"}
        
        val = int(train_num)
        last_digit = val % 10
        
        # 방향 판별
        direction = "다대포행(상선)" if last_digit % 2 == 1 else "노포행(하선)"
        
        # 종별 판별
        type_map = {
            (1, 2): "영업",
            (3, 4): "회송",
            (5, 6): "시운전",
            (7, 8): "단기",
            (9,): "임시"
        }
        t_type = next((v for k, v in type_map.items() if last_digit in k), "미확인")
        
        if last_digit == 9:
            direction = "다대포행(상선)"
        
        return {"num": str(val), "type": t_type, "direction": direction}
    
    def get_schedule_remarks(self, train_details, diagram_type=None, candidates=None, current_date=None):
        """
        입고/주박 추적 정보 생성
        
        Args:
            train_details (dict): 열차 상세 정보
            diagram_type (str): 다이어그램 타입
            candidates (list): 후보 열차 리스트
            current_date (date): 기준 날짜
            
        Returns:
            str: 비고 텍스트 (입고 정보 등)
        """
        from datetime import datetime
        
        train_num = train_details.get("num")
        
        # 날짜 객체 처리
        if not current_date:
            current_date = datetime.now().date()
        if isinstance(current_date, datetime):
            current_date = current_date.date()
        
        # 1. 후보군 처리
        if not train_num and candidates:
            parts = ["[후보 열차]"]
            for c in candidates:
                cdir = "상" if c.get('direction') == 'up' else "하"
                dia = c.get('duty_id', '')
                prefix = f"{dia}다이아 " if dia != '' else ""
                parts.append(f"{prefix}{c['train_number']}({c['time'][:5]}, {cdir})")
            return "\n".join(parts)
        
        # 2. 정상 열차 추적
        if train_num and self.timetable and self.timetable.is_loaded:
            if not diagram_type:
                diagram_type = DateScheduleUtils.get_timetable_diag_type(current_date)
            
            current_sched = self.timetable.search_train(train_number=train_num, diagram_type=diagram_type)
            
            if current_sched:
                duty_id = current_sched[0].get('duty_id')
                depot_info = self.timetable.find_depot_info(duty_id, diagram_type)
                
                if depot_info:
                    depot_name = depot_info.get('station', '').replace('역', '')
                    last_train_num = int(depot_info.get('train_number'))
                    in_time = depot_info.get('time', '')[:5]
                    
                    # Case A: 일반 기지 입고
                    if "다대포" not in depot_name:
                        return (f"{depot_name}\r\n정기\r\n입고\r\n예정\r\n"
                                f"({current_date.month}/{current_date.day})\r\n"
                                f"#{last_train_num}\r\n{in_time}경")
                    
                    # Case B: 주박지 입고
                    else:
                        stabling_type = DateScheduleUtils.get_stabling_diag_type(current_date)
                        in_list = self.STABLING_MAP["in"].get(stabling_type, [])
                        
                        if last_train_num in in_list:
                            return self._predict_next_day_depot(last_train_num, stabling_type, current_date)
                        else:
                            return (f"{depot_name}\r\n종착\r\n"
                                    f"({current_date.month}/{current_date.day})\r\n"
                                    f"#{last_train_num}\r\n{in_time}경")
                
                # C. 입고 정보 없음
                last_stop = current_sched[-1]
                dest = last_stop.get('station', '')
                return f"운행:\r\n{dest}행"
        
        direction = train_details.get("direction", "")
        return f"운행: {direction}" if direction else ""
    
    def _predict_next_day_depot(self, train_num, today_type, current_date):
        """익일 입고 추적"""
        try:
            in_idx = self.STABLING_MAP["in"][today_type].index(train_num)
        except ValueError:
            return "주박\r\n정보\r\n오류"
        
        next_date = current_date + timedelta(days=1)
        next_stabling_type = DateScheduleUtils.get_stabling_diag_type(next_date)
        
        map_in_to_out = {0: 3, 1: 2, 2: 0, 3: 1}
        out_idx = map_in_to_out.get(in_idx)
        
        out_list = self.STABLING_MAP["out"].get(next_stabling_type)
        if not out_list:
            if self.logger:
                self.logger.debug("익일정보없음")
            return ""
        
        next_train_num = out_list[out_idx]
        
        # 익일 타임테이블 타입
        next_timetable_type = 'weekday' if next_stabling_type == 'weekday' else 'weekend'
        if next_stabling_type == 'holiday':
            next_timetable_type = 'holiday'
        
        if not self.timetable:
            return ""
        
        next_sched = self.timetable.search_train(train_number=next_train_num, diagram_type=next_timetable_type)
        
        if next_sched:
            duty_id = next_sched[0].get('duty_id')
            depot_info = self.timetable.find_depot_info(duty_id, next_timetable_type)
            
            if depot_info:
                depot_name = depot_info.get('station', '').replace('역', '')
                last_train_num = int(depot_info.get('train_number'))
                in_time = depot_info.get('time', '')[:5]
                
                return (f"{depot_name}\r\n정기\r\n입고\r\n예정\r\n"
                        f"({next_date.month}/{next_date.day})\r\n"
                        f"#{last_train_num}\r\n{in_time}경")
        
        if self.logger:
            self.logger.debug(f"익일정보없음: {next_train_num}")
        return ""
