import io
import os
import time
import xlwings as xw
import pandas as pd
import polars as pl
from datetime import datetime as _dt
from sqlalchemy import create_engine
from .tunnel import manage_db_tunnel, kill_tunnel

def run_factset_refresh_N_save_to_csv(file_path):
    # 1. FactSet Fix Excel 실행 (Add-in 안정화)
    FIXEXCEL_PATH = r"C:\Program Files (x86)\FactSet\fdswFixExcel.exe"
    if os.path.exists(FIXEXCEL_PATH):
        print("🔧 1. FactSet Fix Excel 실행 중...")
        os.startfile(FIXEXCEL_PATH)
        time.sleep(3) 

    file_name = os.path.basename(file_path)
    print(f"🚀 2. {file_name} 실행 및 로드 대기...")
    os.startfile(file_path)
    
    # 2. 파일 연결 확인 (Logical Blocking)
    start_time = time.time()
    app, wb = None, None
    while time.time() - start_time < 300:
        try:
            for active_app in xw.apps:
                if file_name in [b.name for b in active_app.books]:
                    app = active_app
                    wb = app.books[file_name]
                    # 수리도구가 남긴 빈 문서 정리
                    for b in active_app.books:
                        if b.name.startswith("Book") and b.name != file_name:
                            b.close()
                    break
            if wb: break
        except: pass
        time.sleep(1)
    
    if not wb: raise TimeoutError("🔥 엑셀 파일 연결에 실패했습니다.")

    try:
        # 3. FactSet 공식 매크로 호출 (전체 재계산)
        print("⚡ 3. FactSet 전체 재계산 실행 (FDS_RECALC_NOW)...")
        time.sleep(1) # Add-in 로딩 보장
        
        app.activate(steal_focus=True)
        # 공식 매크로 호출
        app.api.Run("FDS_RECALC_NOW")
        
        # 4. 데이터 확정 모니터링 (7번째 행 감시)
        print("⏳ 4. 데이터 계산 완료 확인 중 (#Calc 탈출 감시)...")
        sheet = wb.sheets[-1]
        for i in range(180):
            # 7행 전체 스캔 (A7:CZ7)
            row_vals = sheet.range("A7:CZ7").value
            str_vals = [str(v).strip() for v in row_vals if v is not None]
            
            # #Calc가 없고, 유효한 값(숫자 또는 #N/A)이 존재할 때 탈출
            if all("#Calc" not in v for v in str_vals) and len(str_vals) > 0:
                print(f"🎉 데이터 갱신 확인 완료! (소요 시간: {i}초)")
                break
            time.sleep(1)
        else:
            print("⏰ 경고: 계산 대기 시간이 초과되었습니다.")

        # 5. PERSONAL.XLSB 스마트 카피 실행
        print("🚀 5. 스마트 카피 매크로 실행 (CSV 내보내기)...")
        app.api.Run("PERSONAL.XLSB!Export_FactSet_Smart_Copy")
        
        # 6. 프로세스 종료
        wb.close()
        print("💾 6. 작업 파일 닫기 및 모든 자동화 프로세스 종료.")

    except Exception as e:
        print(f"🔥 에러 발생: {e}")

def upload_index_DataFrame_with_polars(df: pd.DataFrame,  db_user: str, db_password: str, local_host=False, table_name: str = "adjusted_time_series_data_index", truncate=False):
    """
    Index DataFrame (MultiIndex columns)을 DB에 Upsert.
    테이블이 없으면 자동 생성.

    Parameters:
    - df: pandas DataFrame with MultiIndex columns, index=time
          지원 형식:
          - 3-level: (ticker, index_name, item_name)
          - 2-level: (ticker, item_name) → index_name은 NULL로 저장
    - table_name: Target table name
    - truncate: True이면 insert 전에 테이블을 TRUNCATE (기존 데이터 전체 삭제)
    """
    tunnel_proc = None
    try:
        if not local_host:
            tunnel_proc = manage_db_tunnel()
            if tunnel_proc is None:
                print("🚨 터널 연결 실패.")
                return None

        uri = f"postgresql://{db_user}:{db_password}@127.0.0.1:5432/quant_data"
        engine = create_engine(uri)

        INDEX_COL_MAP = {
            "FG_PRICE_OPEN": "open",
            "FG_PRICE_LOW": "low",
            "FG_PRICE_HIGH": "high",
            "FG_PRICE": "close_pr",
            "FG_TOTAL_RET_IDX": "close_tr",
        }

        value_names = list(INDEX_COL_MAP.values())

        print(f"🚀 Upload 시작: {table_name}")

        # 1. 테이블 생성 (IF NOT EXISTS)
        print("  [1/4] 테이블 확인/생성 중...")
        conn = engine.raw_connection()
        try:
            with conn.cursor() as cur:
                cur.execute(f"""
                    CREATE TABLE IF NOT EXISTS {table_name} (
                        time       TIMESTAMPTZ      NOT NULL,
                        ticker     TEXT             NOT NULL,
                        index_name TEXT,
                        open       DOUBLE PRECISION,
                        low        DOUBLE PRECISION,
                        high       DOUBLE PRECISION,
                        close_pr   DOUBLE PRECISION,
                        close_tr   DOUBLE PRECISION,
                        PRIMARY KEY (time, ticker)
                    );
                """)
                cur.execute(f"""
                    SELECT EXISTS (
                        SELECT 1 FROM timescaledb_information.hypertables
                        WHERE hypertable_name = '{table_name}'
                    );
                """)
                is_hypertable = cur.fetchone()[0]
                if not is_hypertable:
                    cur.execute(f"SELECT create_hypertable('{table_name}', 'time', if_not_exists => TRUE);")
                    cur.execute(f"CREATE INDEX IF NOT EXISTS idx_{table_name}_ticker ON {table_name} (ticker);")
                conn.commit()
        finally:
            conn.close()

        # 2. DataFrame → Polars 변환
        print("  [2/4] DataFrame 변환 중...")
        df_copy = df.copy()

        # ticker → index_name 매핑 추출 & columns flatten
        if df_copy.columns.nlevels == 3:
            # (ticker, index_name, item_name)
            ticker_index_name_map = {ticker: idx_name for ticker, idx_name, _ in df_copy.columns}
            df_copy.columns = [f"{ticker}|{INDEX_COL_MAP[item_name]}" for ticker, _, item_name in df_copy.columns]
        else:
            # (ticker, item_name)
            ticker_index_name_map = {}
            df_copy.columns = [f"{ticker}|{INDEX_COL_MAP[item_name]}" for ticker, item_name in df_copy.columns]

        p_df = pl.from_pandas(df_copy.reset_index())
        p_long = p_df.unpivot(index="index", variable_name="info", value_name="value")
        p_long = p_long.filter(pl.col("value").is_not_null())
        p_long = p_long.with_columns(
            pl.col("info").str.split("|").alias("_split")
        ).with_columns([
            pl.col("_split").list.get(0).alias("ticker"),
            pl.col("_split").list.get(1).alias("item_name"),
        ]).drop("_split", "info")

        # 3. Pivot → wide format + index_name join
        print("  [3/4] Pivot 변환 중...")
        wide_df = p_long.pivot(values="value", index=["index", "ticker"], on="item_name")

        # ticker별 항목 수가 다를 수 있으므로 없는 컬럼은 null로 추가
        for col in value_names:
            if col not in wide_df.columns:
                wide_df = wide_df.with_columns(pl.lit(None).cast(pl.Float64).alias(col))

        # index_name 매핑 join
        if ticker_index_name_map:
            map_df = pl.DataFrame({"ticker": list(ticker_index_name_map.keys()), "index_name": list(ticker_index_name_map.values())})
            wide_df = wide_df.join(map_df, on="ticker", how="left")
        else:
            wide_df = wide_df.with_columns(pl.lit(None).cast(pl.Utf8).alias("index_name"))

        final_df = wide_df.rename({"index": "time"}).select(["time", "ticker", "index_name"] + value_names)
        print(f"        - 최종 데이터: {len(final_df):,}건")

        # 4. CSV 버퍼 → COPY → UPSERT
        print(f"  [4/4] DB {'Truncate + Insert' if truncate else 'Upsert'} 실행 중... ({_dt.now().strftime('%H:%M')})")
        buffer = io.BytesIO()
        final_df.write_csv(buffer, include_header=False, separator='\t')
        buffer.seek(0)

        all_cols = ["time", "ticker", "index_name"] + value_names
        update_cols_list = ["index_name"] + value_names
        conn = engine.raw_connection()
        try:
            with conn.cursor() as cur:
                if truncate:
                    cur.execute(f"TRUNCATE {table_name}")
                    cur.copy_from(buffer, table_name, sep="\t", null="", columns=all_cols)
                else:
                    cur.execute(f"CREATE TEMP TABLE temp_{table_name} (LIKE {table_name} INCLUDING DEFAULTS) ON COMMIT DROP")
                    cur.copy_from(buffer, f"temp_{table_name}", sep="\t", null="", columns=all_cols)

                    update_cols = ", ".join([f"{col} = EXCLUDED.{col}" for col in update_cols_list])
                    cols_str = ", ".join(all_cols)
                    upsert_query = f"""
                    INSERT INTO {table_name} ({cols_str})
                    SELECT {cols_str} FROM temp_{table_name}
                    ON CONFLICT (time, ticker) DO UPDATE SET
                        {update_cols};
                    """
                    cur.execute(upsert_query)
                conn.commit()
                print(f"✅ 완료! {len(final_df):,}건 {'Insert' if truncate else 'Upsert'} 성공 ({_dt.now().strftime('%H:%M')})")
        finally:
            conn.close()

        return final_df

    finally:
        if tunnel_proc is not None:
            kill_tunnel(tunnel_proc)