# app/services/studyplan_excel_generator.py

"""
학습계획표 엑셀 파일 생성
BASE 엑셀 템플릿 생성 및 데이터 채우기
"""

from openpyxl import Workbook
from openpyxl.styles import Font, Alignment, PatternFill, Border, Side
from openpyxl.utils import get_column_letter
from openpyxl.worksheet.page import PageMargins, PrintPageSetup
from pathlib import Path


def create_base_excel_template(output_path: str = None):
    """
    BASE 엑셀 템플릿 생성
    
    Args:
        output_path: 저장할 경로 (없으면 기본 경로)
    
    Returns:
        엑셀 파일 경로
    """
    if output_path is None:
        base_dir = Path(__file__).resolve().parent.parent.parent
        output_path = base_dir / "studyplan_files" / "base_studyplan_template.xlsx"
    
    # 디렉토리 생성
    Path(output_path).parent.mkdir(parents=True, exist_ok=True)
    
    # 워크북 생성
    wb = Workbook()
    ws = wb.active
    ws.title = "学習計画表"
    
    # 스타일 정의
    title_font = Font(name="メイリオ", size=18, bold=True, color="FFFFFF")
    header_font = Font(name="メイリオ", size=12, bold=True, color="FFFFFF")
    normal_font = Font(name="メイリオ", size=11)
    
    header_fill = PatternFill(start_color="4472C4", end_color="4472C4", fill_type="solid")
    info_fill = PatternFill(start_color="D9E1F2", end_color="D9E1F2", fill_type="solid")
    weekday_fill = PatternFill(start_color="E7F3FF", end_color="E7F3FF", fill_type="solid")
    weekend_fill = PatternFill(start_color="FFF2CC", end_color="FFF2CC", fill_type="solid")
    
    thin_border = Border(
        left=Side(style='thin'),
        right=Side(style='thin'),
        top=Side(style='thin'),
        bottom=Side(style='thin')
    )
    
    # 1. 제목
    ws.merge_cells('A1:D1')
    ws['A1'] = "学習計画表"
    ws['A1'].font = title_font
    ws['A1'].fill = header_fill
    ws['A1'].alignment = Alignment(horizontal='center', vertical='center')
    ws.row_dimensions[1].height = 30
    
    # 2. 기본 정보 섹션
    ws['A3'] = "【基本情報】"
    ws['A3'].font = header_font
    ws['A3'].fill = header_fill
    ws['A3'].alignment = Alignment(horizontal='left', vertical='center')
    ws.merge_cells('A3:D3')
    ws.row_dimensions[3].height = 25
    
    # 기본 정보 항목들
    info_items = [
        ("目標期間", "B4", "C4"),
        ("現在の状態", "B5", "C5"),
        ("平日学習時間", "B6", "C6"),
        ("週末学習時間", "B7", "C7"),
        ("希望時間帯", "B8", "C8")
    ]
    
    row = 4
    for label, label_cell, value_cell in info_items:
        ws[label_cell] = label + ":"
        ws[label_cell].font = Font(name="メイリオ", size=11, bold=True)
        ws[label_cell].fill = info_fill
        ws[label_cell].alignment = Alignment(horizontal='left', vertical='center')
        ws[label_cell].border = thin_border
        
        # 값 셀 (C열)과 오른쪽 테두리 셀 (D열) 모두 설정
        ws[value_cell] = ""  # 값은 나중에 채움
        ws[value_cell].font = normal_font
        ws[value_cell].fill = info_fill
        ws[value_cell].alignment = Alignment(horizontal='left', vertical='center')
        ws[value_cell].border = thin_border
        
        # D열에도 테두리 추가 (오른쪽 테두리 보완)
        d_cell = f'D{row}'
        ws[d_cell] = ""
        ws[d_cell].fill = info_fill
        ws[d_cell].border = thin_border
        
        ws.row_dimensions[row].height = 22
        row += 1
    
    # 3. 주간 시간표 섹션
    ws['A10'] = "【週間スケジュール】"
    ws['A10'].font = header_font
    ws['A10'].fill = header_fill
    ws['A10'].alignment = Alignment(horizontal='left', vertical='center')
    ws.merge_cells('A10:D10')
    ws.row_dimensions[10].height = 25
    
    ws['A11'] = "(このパターンを目標期間中繰り返します)"
    ws['A11'].font = Font(name="メイリオ", size=10, italic=True, color="666666")
    ws.merge_cells('A11:D11')
    ws.row_dimensions[11].height = 18
    
    # 테이블 헤더
    headers = ["曜日", "学習時間", "時間帯"]
    header_cols = ['A', 'B', 'C']
    
    for col, header in zip(header_cols, headers):
        cell = ws[f'{col}12']
        cell.value = header
        cell.font = header_font
        cell.fill = header_fill
        cell.alignment = Alignment(horizontal='center', vertical='center')
        cell.border = thin_border
    
    ws.row_dimensions[12].height = 25
    
    # 요일 데이터 (템플릿이므로 빈 값)
    weekdays = ['月曜日', '火曜日', '水曜日', '木曜日', '金曜日', '土曜日', '日曜日']
    
    for idx, day in enumerate(weekdays, start=13):
        # 요일
        ws[f'A{idx}'] = day
        ws[f'A{idx}'].font = normal_font
        ws[f'A{idx}'].fill = weekday_fill if idx < 18 else weekend_fill
        ws[f'A{idx}'].alignment = Alignment(horizontal='left', vertical='center')
        ws[f'A{idx}'].border = thin_border
        
        # 학습 시간 (나중에 채움)
        ws[f'B{idx}'] = ""
        ws[f'B{idx}'].font = normal_font
        ws[f'B{idx}'].fill = weekday_fill if idx < 18 else weekend_fill
        ws[f'B{idx}'].alignment = Alignment(horizontal='center', vertical='center')
        ws[f'B{idx}'].border = thin_border
        
        # 시간대 (나중에 채움)
        ws[f'C{idx}'] = ""
        ws[f'C{idx}'].font = normal_font
        ws[f'C{idx}'].fill = weekday_fill if idx < 18 else weekend_fill
        ws[f'C{idx}'].alignment = Alignment(horizontal='left', vertical='center')
        ws[f'C{idx}'].border = thin_border
        
        # D열에도 테두리 추가 (오른쪽 테두리 보완)
        ws[f'D{idx}'] = ""
        ws[f'D{idx}'].fill = weekday_fill if idx < 18 else weekend_fill
        ws[f'D{idx}'].border = thin_border
        
        ws.row_dimensions[idx].height = 22
    
    # 열 너비 조정
    ws.column_dimensions['A'].width = 15
    ws.column_dimensions['B'].width = 15
    ws.column_dimensions['C'].width = 35
    ws.column_dimensions['D'].width = 5
    
    # 인쇄 설정 추가 (중앙 정렬, 여백 설정)
    # 여백을 조금 줄여서 내용이 잘 들어오게 하기 (0.5인치 = 약 1.2cm)
    ws.page_margins = PageMargins(
        left=0.5,   # 0.5인치 (약 1.2cm)
        right=0.5,  # 0.5인치
        top=0.5,    # 0.5인치
        bottom=0.5, # 0.5인치
        header=0.3,  # 헤더 여백
        footer=0.3   # 푸터 여백
    )
    
    # 페이지 설정 (가로/세로 중앙 정렬)
    ws.page_setup = PrintPageSetup(
        orientation='landscape',  # 가로 방향
        paperSize=9,  # A4 용지 (9 = A4, 210mm x 297mm)
        fitToHeight=1,  # 1페이지에 맞추기
        fitToWidth=1    # 1페이지에 맞추기
    )
    
    # 가로/세로 중앙 정렬 설정 (좌우 대칭을 위해)
    ws.page_setup.horizontalCentered = True
    ws.page_setup.verticalCentered = True
    
    # fitToHeight와 fitToWidth가 설정되면 자동으로 fitToPage가 활성화됨
    # fitToPage 속성은 직접 설정 시 오류가 발생할 수 있으므로 제거
    
    # 인쇄 영역 설정 (A1:D19 정도) - 1페이지에 맞추기
    ws.print_area = 'A1:D19'
    
    # 페이지 나누기 제거 (1페이지로 유지)
    ws.page_breaks = []
    
    # 1. 행 강제 삭제 (조건문 없이 실행)
    # 20행부터 100개 행을 무조건 삭제하여 아래쪽의 잔여 서식/테두리를 완전히 제거
    try:
        ws.delete_rows(20, 100)
        print("BASE Template: Force deleted rows 20-120 to clear ghost styling")
    except Exception as e:
        print(f"BASE Template: Could not delete rows 20-120: {e}")

    # 2. 열 강제 삭제
    # E열(5번째)부터 20개 열을 무조건 삭제
    try:
        ws.delete_cols(5, 20)
        print("BASE Template: Force deleted cols E-X to clear ghost styling")
    except Exception as e:
        print(f"BASE Template: Could not delete cols E-X: {e}")

    # 인쇄 영역을 명시적으로 설정 (A1:D19만 인쇄)
    ws.print_area = 'A1:D19'
    
    # 페이지 설정: 1페이지에 정확히 맞추기
    ws.page_setup = PrintPageSetup(
        orientation='landscape',  # 가로 방향
        paperSize=9,  # A4 (210mm x 297mm)
        fitToHeight=1,  # 정확히 1페이지 높이
        fitToWidth=1    # 정확히 1페이지 너비
    )
    
    # 가로/세로 중앙 정렬 설정 (좌우 대칭을 위해)
    ws.page_setup.horizontalCentered = True
    ws.page_setup.verticalCentered = True
    
    # fitToHeight와 fitToWidth가 설정되면 자동으로 fitToPage가 활성화됨
    # fitToPage 속성은 직접 설정 시 오류가 발생할 수 있으므로 제거
    
    # [선택] 여백을 조금 줄여서 내용이 잘 들어오게 하기
    # 좌우상하 여백을 0.5인치(약 1.2cm)로 설정
    ws.page_margins = PageMargins(left=0.5, right=0.5, top=0.5, bottom=0.5, header=0.3, footer=0.3)
    
    # 페이지 나누기 완전히 제거
    ws.page_breaks = []
    
    # 저장 전에 사용 영역 확인
    print(f"BASE Template before save - max_row={ws.max_row}, max_column={ws.max_column}, print_area={ws.print_area}")
    
    # 저장
    wb.save(output_path)
    
    # 저장 후 다시 로드하여 확인
    from openpyxl import load_workbook
    wb_check = load_workbook(output_path)
    ws_check = wb_check.active
    print(f"BASE Template after save and reload - max_row={ws_check.max_row}, max_column={ws_check.max_column}, print_area={ws_check.print_area}")
    wb_check.close()
    
    return str(output_path)


def _convert_time_slot_to_range(time_slot_text: str, hours: int = None) -> str:
    """
    시간대 텍스트를 시간 범위 형식으로 변환
    학습 시간(hours)이 주어지면 시작 시간부터 해당 시간만큼 계산
    
    Args:
        time_slot_text: 시간대 텍스트 (예: "夕方")
        hours: 학습 시간 (예: 1, 3) - None이면 기본 범위 반환
    
    Returns:
        시간 범위 (예: "17:00~18:00" 또는 "17:00~20:00")
    """
    time_slot_lower = time_slot_text.lower().strip()
    
    # 시간대 매핑 (시작 시간)
    time_slot_start_map = {
        "朝": "06:00",
        "午前": "09:00",
        "昼": "12:00",
        "午後": "14:00",
        "夕方": "17:00",
        "夜間": "19:00",
        "深夜": "22:00",
        "夜間/深夜": "19:00",
    }
    
    # 시작 시간 찾기
    start_time = None
    for key, value in time_slot_start_map.items():
        if key in time_slot_text:
            start_time = value
            break
    
    # 시작 시간을 찾았고 학습 시간이 주어진 경우
    if start_time and hours is not None:
        # 시작 시간을 분으로 변환
        start_hour, start_min = map(int, start_time.split(':'))
        start_minutes = start_hour * 60 + start_min
        
        # 종료 시간 계산
        end_minutes = start_minutes + (hours * 60)
        end_hour = (end_minutes // 60) % 24
        end_min = end_minutes % 60
        
        end_time = f"{end_hour:02d}:{end_min:02d}"
        return f"{start_time}~{end_time}"
    
    # 학습 시간이 없거나 시작 시간을 찾지 못한 경우 기본 범위 반환
    time_slot_map = {
        "朝": "06:00~09:00",
        "午前": "09:00~12:00",
        "昼": "12:00~14:00",
        "午後": "14:00~17:00",
        "夕方": "17:00~19:00",
        "夜間": "19:00~22:00",
        "深夜": "22:00~02:00",
        "夜間/深夜": "19:00~02:00",
        "夜間/深夜（19:00〜02:00）": "19:00~02:00",
    }
    
    # 정확히 매칭되는 경우
    for key, value in time_slot_map.items():
        if key in time_slot_text:
            return value
    
    # 이미 시간 범위 형식인 경우 (예: "19:00~20:00")
    if "~" in time_slot_text or "〜" in time_slot_text or ":" in time_slot_text:
        # "~" 또는 "〜"로 통일
        time_slot_text = time_slot_text.replace("〜", "~")
        return time_slot_text
    
    # 매칭되지 않으면 원본 반환
    return time_slot_text


def _get_merged_cell_start(ws, cell_coordinate):
    """
    병합된 셀인 경우 병합의 시작 셀 좌표를 반환
    병합되지 않은 셀이면 그대로 반환
    """
    from openpyxl.utils import get_column_letter
    from openpyxl.utils.cell import coordinate_from_string, column_index_from_string
    
    try:
        # 셀 좌표를 행/열로 변환
        col, row = coordinate_from_string(cell_coordinate)
        col_idx = column_index_from_string(col)
        
        # 병합된 셀 범위 확인
        for merged_range in ws.merged_cells.ranges:
            # 셀이 병합 범위 안에 있는지 확인
            if (merged_range.min_row <= row <= merged_range.max_row and
                merged_range.min_col <= col_idx <= merged_range.max_col):
                # 병합 범위의 시작 셀 좌표 생성
                start_col = get_column_letter(merged_range.min_col)
                start_row = merged_range.min_row
                return f"{start_col}{start_row}"
    except Exception as e:
        # 에러 발생 시 원래 셀 좌표 반환
        pass
    
    # 병합되지 않은 셀이면 그대로 반환
    return cell_coordinate


def fill_excel_with_data(excel_path: str, total_duration: str, current_status: str,
                        weekday_hours: int, weekend_hours: int, preferred_time_slot: str) -> str:
    """
    BASE 엑셀 템플릿에 데이터 채우기
    
    Args:
        excel_path: 엑셀 파일 경로
        total_duration: 목표 기간
        current_status: 현재 상태
        weekday_hours: 평일 학습 시간
        weekend_hours: 주말 학습 시간
        preferred_time_slot: 선호 시간대
    
    Returns:
        채워진 엑셀 파일 경로
    """
    from openpyxl import load_workbook
    
    # BASE 템플릿 로드
    wb = load_workbook(excel_path)
    ws = wb.active
    
    # 기본 정보 채우기 (병합된 셀 처리)
    # 병합된 셀에 값을 쓰기 위해 병합을 해제하고 값을 쓴 후 다시 병합
    def safe_write_cell(cell_coord, value):
        """병합된 셀도 안전하게 값을 쓰는 함수"""
        from openpyxl.utils.cell import coordinate_from_string, column_index_from_string
        from app.core.config import logger
        
        try:
            # 먼저 병합된 셀인지 확인하고 시작 셀 찾기
            start_cell = _get_merged_cell_start(ws, cell_coord)
            
            # 해당 셀과 관련된 모든 병합 범위 찾기
            merged_ranges_to_restore = []
            
            col, row = coordinate_from_string(start_cell)
            col_idx = column_index_from_string(col)
            
            # 해당 셀을 포함하는 병합 범위 찾기
            for merged_range in list(ws.merged_cells.ranges):
                if (merged_range.min_row <= row <= merged_range.max_row and
                    merged_range.min_col <= col_idx <= merged_range.max_col):
                    # 병합 범위 저장 (나중에 복원하기 위해)
                    merged_ranges_to_restore.append(str(merged_range))
                    # 병합 해제
                    ws.unmerge_cells(str(merged_range))
            
            # 시작 셀에 값 쓰기
            ws[start_cell] = value
            logger.debug(f"Successfully wrote {value} to {start_cell} (original: {cell_coord})")
            
            # 병합 복원
            for merged_range_str in merged_ranges_to_restore:
                ws.merge_cells(merged_range_str)
                
        except Exception as e:
            # 에러 발생 시 로그 남기고 값만 쓰기 시도
            logger.warning(f"Error writing to {cell_coord}: {e}, trying direct write")
            try:
                # 시작 셀 찾기
                start_cell = _get_merged_cell_start(ws, cell_coord)
                ws[start_cell] = value
                logger.debug(f"Direct write succeeded: {value} to {start_cell}")
            except Exception as e2:
                logger.error(f"Failed to write to {cell_coord}: {e2}")
                # 최후의 수단: 병합 무시하고 직접 쓰기
                try:
                    ws[cell_coord].value = value
                except:
                    raise Exception(f"Cannot write to {cell_coord}: {e2}")
    
    # 시간대 변환 함수 사용
    # 기본 정보에는 원본 텍스트, 주간 시간표에는 학습 시간에 맞춘 시간 범위 표시
    time_slot_for_summary = preferred_time_slot  # 원본 텍스트 (예: "夕方")
    
    # 로그 추가 (디버깅용)
    from app.core.config import logger
    logger.info(f"=== Filling Excel ===")
    logger.info(f"Received values: weekday_hours={weekday_hours} (type: {type(weekday_hours)}), weekend_hours={weekend_hours} (type: {type(weekend_hours)})")
    logger.info(f"Duration={total_duration}, Status={current_status}")
    logger.info(f"Time slot: {time_slot_for_summary} (will be calculated per weekday/weekend)")
    logger.info(f"====================")
    
    # 기본 정보 채우기
    # A열: 라벨, B열: 값 (사용자가 엑셀을 수정함)
    try:
        target_cell = _get_merged_cell_start(ws, 'B4')
        safe_write_cell(target_cell, total_duration)
        logger.info(f"Wrote {target_cell} (B4): {total_duration}")
    except Exception as e:
        logger.error(f"Error writing B4: {e}")
        raise
    
    try:
        target_cell = _get_merged_cell_start(ws, 'B5')
        safe_write_cell(target_cell, current_status)
        logger.info(f"Wrote {target_cell} (B5): {current_status}")
    except Exception as e:
        logger.error(f"Error writing B5: {e}")
        raise
    
    try:
        target_cell = _get_merged_cell_start(ws, 'B6')
        safe_write_cell(target_cell, f"{weekday_hours}時間")
        logger.info(f"Wrote {target_cell} (B6): {weekday_hours}時間")
    except Exception as e:
        logger.error(f"Error writing B6: {e}")
        raise
    
    try:
        target_cell = _get_merged_cell_start(ws, 'B7')
        safe_write_cell(target_cell, f"{weekend_hours}時間")
        logger.info(f"Wrote {target_cell} (B7): {weekend_hours}時間")
    except Exception as e:
        logger.error(f"Error writing B7: {e}")
        raise
    
    try:
        target_cell = _get_merged_cell_start(ws, 'B8')
        safe_write_cell(target_cell, time_slot_for_summary)  # 원본 텍스트 (예: "夕方")
        logger.info(f"Wrote {target_cell} (B8): {time_slot_for_summary}")
    except Exception as e:
        logger.error(f"Error writing B8: {e}")
        raise
    
    # 주간 시간표 채우기
    weekdays = ['月曜日', '火曜日', '水曜日', '木曜日', '金曜日', '土曜日', '日曜日']
    
    for idx, day in enumerate(weekdays, start=13):
        b_cell = f'B{idx}'
        c_cell = f'C{idx}'
        
        try:
            if idx < 18:  # 평일 (월~금)
                safe_write_cell(b_cell, f"{weekday_hours}時間")
                # 평일 시간대: 학습 시간에 맞춘 범위 계산
                time_slot_weekday = _convert_time_slot_to_range(preferred_time_slot, weekday_hours)
                safe_write_cell(c_cell, time_slot_weekday)
                logger.info(f"Wrote {b_cell}: {weekday_hours}時間, {c_cell}: {time_slot_weekday} (weekday)")
            else:  # 주말 (토~일)
                safe_write_cell(b_cell, f"{weekend_hours}時間")
                # 주말 시간대: 학습 시간에 맞춘 범위 계산
                time_slot_weekend = _convert_time_slot_to_range(preferred_time_slot, weekend_hours)
                safe_write_cell(c_cell, time_slot_weekend)
                logger.info(f"Wrote {b_cell}: {weekend_hours}時間, {c_cell}: {time_slot_weekend} (weekend)")
        except Exception as e:
            logger.error(f"Error writing {b_cell} or {c_cell}: {e}")
            raise
    
    # 1. 행 강제 삭제 (조건문 없이 실행)
    # 20행부터 100개 행을 무조건 삭제하여 아래쪽의 잔여 서식/테두리를 완전히 제거
    try:
        ws.delete_rows(20, 100)
        logger.info("Force deleted rows 20-120 to clear ghost styling")
    except Exception as e:
        logger.warning(f"Could not delete rows 20-120: {e}")

    # 2. 열 강제 삭제
    # E열(5번째)부터 20개 열을 무조건 삭제
    try:
        ws.delete_cols(5, 20)
        logger.info("Force deleted cols E-X to clear ghost styling")
    except Exception as e:
        logger.warning(f"Could not delete cols E-X: {e}")

    # 인쇄 영역을 다시 명시적으로 설정 (A1:D19만 인쇄)
    ws.print_area = 'A1:D19'
    
    # 페이지 설정 재확인
    ws.page_setup = PrintPageSetup(
        orientation='landscape',  # 가로 방향
        paperSize=9,  # A4
        fitToHeight=1,  # 정확히 1페이지
        fitToWidth=1    # 정확히 1페이지
    )
    
    # fitToHeight와 fitToWidth가 설정되면 자동으로 fitToPage가 활성화됨
    # fitToPage 속성은 직접 설정 시 오류가 발생할 수 있으므로 제거
    
    # [선택] 여백을 조금 줄여서 내용이 잘 들어오게 하기
    # 좌우상하 여백을 0.5인치(약 1.2cm)로 설정
    # ws.page_margins = PageMargins(left=0.5, right=0.5, top=0.5, bottom=0.5, header=0.3, footer=0.3)
    
    # 페이지 나누기 완전히 제거
    ws.page_breaks = []
    
    # 저장 전에 사용 영역 확인 및 로그
    logger.info(f"=== Excel File Info Before Save ===")
    logger.info(f"max_row={ws.max_row}, max_column={ws.max_column}")
    logger.info(f"print_area={ws.print_area}")
    logger.info(f"page_setup.fitToHeight={ws.page_setup.fitToHeight}, fitToWidth={ws.page_setup.fitToWidth}")
    logger.info(f"page_setup.orientation={ws.page_setup.orientation}")
    logger.info(f"page_breaks count={len(ws.page_breaks)}")
    
    # 저장
    wb.save(excel_path)
    logger.info(f"Excel file saved to: {excel_path}")
    
    # 저장 후 다시 로드하여 확인 (LibreOffice가 읽을 때와 동일한 상태 확인)
    from openpyxl import load_workbook
    wb_check = load_workbook(excel_path)
    ws_check = wb_check.active
    logger.info(f"=== Excel File Info After Reload ===")
    logger.info(f"max_row={ws_check.max_row}, max_column={ws_check.max_column}")
    logger.info(f"print_area={ws_check.print_area}")
    logger.info(f"page_setup.fitToHeight={ws_check.page_setup.fitToHeight}, fitToWidth={ws_check.page_setup.fitToWidth}")
    logger.info(f"=====================================")
    wb_check.close()
    
    return excel_path
