import pandas as pd
import re
from datetime import datetime


def subset_id_with_kcd_terms(claims_df, start, end, kcd_pattern):
    """
    claims_df를 받아 주어진 시작일~종료일까지 기간 동안
    정규표현식으로 설정한 kcd코드가 포함된 데이터를 반환하는 함수.
    
    성능 최적화:
    - 날짜 변환 최소화 (한 번만 변환)
    - 벡터화 연산 활용
    - 정규표현식 컴파일 최소화
    - 불필요한 데이터 복사 최소화
    
    Parameters:
    -----------
    claims_df : pd.DataFrame
        청구 데이터프레임. 'kcd', 'sdate', 'edate' 컬럼이 있어야 함
    start : str or datetime
        시작일 (yyyy-mm-dd 형식의 문자열 또는 datetime 객체)
    end : str or datetime
        종료일 (yyyy-mm-dd 형식의 문자열 또는 datetime 객체)
    kcd_pattern : str
        kcd 코드를 판별하는 정규표현식 패턴
        예: r'^C[0-9]' (C로 시작하는 코드), r'^I6[0-9]' (I6으로 시작하는 코드)
    
    Returns:
    --------
    pd.DataFrame
        시작일~종료일 기간 내에서 정규표현식에 매칭되는 kcd 코드를 가진 데이터만 필터링한 결과
        원본 claims_df의 모든 컬럼을 유지
    """
    if len(claims_df) == 0:
        return claims_df.copy()
    
    # 필수 컬럼 확인
    required_cols = ['kcd', 'sdate', 'edate']
    missing_cols = [col for col in required_cols if col not in claims_df.columns]
    if missing_cols:
        raise ValueError(f"필수 컬럼이 없습니다: {missing_cols}")
    
    # 날짜 변환 (한 번만)
    start_dt = pd.to_datetime(start)
    end_dt = pd.to_datetime(end)
    
    if start_dt > end_dt:
        raise ValueError("start 날짜가 end 날짜보다 늦을 수 없습니다.")
    
    # 날짜 변환 (한 번만)
    sdate_dt = pd.to_datetime(claims_df['sdate'])
    edate_dt = pd.to_datetime(claims_df['edate'])
    
    # 1. 기간 내 데이터 필터링 (sdate <= end AND edate >= start)
    period_mask = (sdate_dt <= end_dt) & (edate_dt >= start_dt)
    
    if not period_mask.any():
        # 기간 내 데이터가 없으면 빈 DataFrame 반환 (원본 구조 유지)
        return claims_df.iloc[0:0].copy()
    
    # 2. kcd 코드 정규표현식 매칭
    # 정규표현식 컴파일 (한 번만)
    pattern_re = re.compile(kcd_pattern)
    
    # kcd 코드를 문자열로 변환 (한 번만)
    kcd_str = claims_df['kcd'].astype(str)
    
    # kcd 코드 매칭 (벡터화 연산)
    kcd_match = kcd_str.apply(lambda x: bool(pattern_re.search(str(x))))
    
    # 3. 두 조건 결합: 기간 내 + kcd 매칭
    combined_mask = period_mask & kcd_match
    
    if not combined_mask.any():
        # 조건을 만족하는 데이터가 없으면 빈 DataFrame 반환 (원본 구조 유지)
        return claims_df.iloc[0:0].copy()
    
    # 필터링된 데이터 반환
    result_df = claims_df[combined_mask].copy()
    
    return result_df
