import pandas as pd
import time
from .base import DataFetcher
from .ifind_client import IFindClient
from storage.file_io import DataStorage

class HkFetcher(DataFetcher):
    def __init__(self, api_key: str):
        # api_key is the iFinD Refresh Token
        super().__init__(api_key)
        self.cli = IFindClient(refresh_token=api_key)
        self.storage = DataStorage()
        self._basic_info_cache = {}

    def _get_ifind_code(self, symbol: str) -> str:
        """保持逻辑一致性，如果是纯数字则补齐后缀 .HK，否则直接传"""
        # Strip .HK suffix if present to handle input like '00700.HK'
        clean_symbol = symbol.replace('.HK', '').replace('.hk', '')
        
        if clean_symbol.isdigit():
            # Force 4 digits for HK (e.g., 700 -> 0700.HK, 00700 -> 0700.HK)
            # e.g. 01651 -> 1651.HK
            code_int = int(clean_symbol)
            normalized_code = str(code_int).zfill(4)
            return f"{normalized_code}.HK"
        return symbol

    def _fetch_basic_info(self, symbol: str) -> dict:
        """获取公司的基本信息：中文名称、会计年结日、上市日期"""
        code = self._get_ifind_code(symbol)
        if code in self._basic_info_cache:
            return self._basic_info_cache[code]

        params = {
            "codes": code,
            "indipara": [
                {"indicator": "corp_cn_name", "indiparams": []},
                {"indicator": "accounting_date", "indiparams": []},
                {"indicator": "ipo_date", "indiparams": []}
            ]
        }
        res = self.cli.post("basic_data_service", params)
        df = self._parse_ifind_tables(res)
        if not df.empty:
            self._save_raw_data(df, symbol, "basic_info_raw")
        
        info = {
            "name": "",
            "accounting_date": "1231", # 默认 12-31
            "ipo_date": ""
        }
        
        if not df.empty:
            row = df.iloc[0]
            info["name"] = str(row.get("corp_cn_name", ""))
            info["acc_date"] = str(row.get("accounting_date", "1231"))
            info["accounting_date"] = "1231"
            info["ipo_date"] = str(row.get("ipo_date", "")).replace("-", "").replace("/", "")

        self._basic_info_cache[code] = info
        return info

    def _save_raw_data(self, data: any, symbol: str, name: str):
        if data is None:
            return
        if isinstance(data, dict):
            df = pd.DataFrame([data])
        else:
            df = data
        self.storage.save_data(df, 'HK', symbol, f"raw_{name}")

    def _parse_ifind_tables(self, res: dict) -> pd.DataFrame:
        """通用解析 iFinD 返回结果的 tables 结构为 DataFrame"""
        if not res:
            return pd.DataFrame()
        
        # Default to 0 if not present (for lenient mocking) or check properly
        error_code = res.get("errorcode", 0)
        if error_code != 0:
            print(f"iFinD API Error: {res.get('errmsg')} (code: {error_code})")
            return pd.DataFrame()
        
        tables = res.get("tables", [])
        if not tables:
            return pd.DataFrame()
        
        table_info = tables[0]
        table_data = table_info.get("table", {})
        times = table_info.get("time", []) 
        
        if not table_data:
            return pd.DataFrame()
            
        processed_table_data = {}
        for k, v in table_data.items():
            if not isinstance(v, list):
                processed_table_data[k] = [v]
            else:
                processed_table_data[k] = v

        df = pd.DataFrame(processed_table_data)
        if times and len(times) == len(df):
            df['end_date'] = [str(t).replace('-', '').replace('/', '').split(' ')[0] for t in times]
        elif times and len(df) == 1:
            df['end_date'] = str(times[0]).replace('-', '').replace('/', '').split(' ')[0]
        
        if 'end_date' not in df.columns:
            for col in ['time', 'date', 'trade_date', 'REPORT_DATE']:
                if col in df.columns:
                    df['end_date'] = df[col].astype(str).str.replace('-', '').str.replace('/', '').str.split(' ').str[0]
                    break
        
        return df

    def _filter_data(self, df: pd.DataFrame) -> pd.DataFrame:
        if df.empty or 'end_date' not in df.columns:
            return df
        
        df = df.sort_values(by='end_date', ascending=False)
        df = df.drop_duplicates(subset=['end_date'], keep='first')
        
        if df.empty:
            return df

        latest_record = df.iloc[[0]]
        try:
            latest_date_str = str(latest_record['end_date'].values[0])
            last_year_date_str = str(int(latest_date_str) - 10000)
            comparable_record = df[df['end_date'].astype(str) == last_year_date_str]
        except:
            comparable_record = pd.DataFrame()

        if comparable_record.empty:
            dfs_to_concat = [latest_record, df]
        else:
            dfs_to_concat = [latest_record, comparable_record, df]

        # HK typically has 1231 or 0331 or 0630 etc. but annual is annual.
        combined = pd.concat(dfs_to_concat) # Include all for now and dedup
        combined = combined.drop_duplicates(subset=['end_date'])
        combined = combined.sort_values(by='end_date', ascending=False)
        return combined

    def _fetch_financial_data_annual(self, symbol: str, indicator_configs: list) -> pd.DataFrame:
        """通用获取历年财务数据 (HKD 为主，但 iFinD 支持转 CNY)"""
        code = self._get_ifind_code(symbol)
        current_year = int(time.strftime("%Y"))
        
        # 1. First, determine the most recent valid year by trying backwards from current year
        last_valid_year = None
        
        # Try up to 3 years back to find the latest available report
        # e.g., in Jan 2026, try 2026 -> fail, 2025 -> success
        for offset in range(3):
            test_year = current_year - offset
            test_date = f"{test_year}1231"
            
            # Use the first indicator to test availability
            first_indicator = indicator_configs[0]
            params = {
                "codes": code,
                "indipara": [
                    {"indicator": first_indicator["indicator"], "indiparams": [test_date, first_indicator.get("type", "1"), "CNY"]}
                ]
            }
            res = self.cli.post("basic_data_service", params)
            df = self._parse_ifind_tables(res)
            
            if not df.empty:
                # Check for non-null values
                valid_val = df.iloc[0, 0] if not df.empty and df.shape[1] > 0 else None
                if pd.notna(valid_val) and valid_val != 0:
                    last_valid_year = test_year
                    break
        
        if last_valid_year is None:
            # Fallback to current year if nothing found (will likely return empty/zeros, but keeps logic flowing)
            last_valid_year = current_year

        # 2. Fetch 5 years starting from the last valid year
        all_dfs = []
        for i in range(5):
            target_year = last_valid_year - i
            target_date = f"{target_year}1231"
            
            params = {
                "codes": code,
                "indipara": [
                    {"indicator": item["indicator"], "indiparams": [target_date, item.get("type", "1"), "CNY"]}
                    for item in indicator_configs
                ]
            }
            res = self.cli.post("basic_data_service", params)
            df = self._parse_ifind_tables(res)
            
            if not df.empty:
                valid_cols = [c for c in df.columns if c not in ['end_date', 'date']]
                if not df[valid_cols].isnull().all().all():
                    df['end_date'] = target_date
                    df = df.dropna(axis=1, how='all')
                    all_dfs.append(df)
        
        if not all_dfs:
            return pd.DataFrame()
        
        return pd.concat(all_dfs, ignore_index=True)

    def get_income_statement(self, symbol: str) -> pd.DataFrame:
        indicators = [
            {"indicator": "total_oi"},
            {"indicator": "prime_oi"},
            {"indicator": "other_oi"},
            {"indicator": "operating_cost"},
            {"indicator": "operating_expense"},
            {"indicator": "operating_fee"},
            {"indicator": "p_depreciation_and_amortization"},
            {"indicator": "gross_profit"},
            {"indicator": "sales_ad_and_ga"},
            {"indicator": "rad_cost"},
            {"indicator": "sales_fee"},
            {"indicator": "financial_expense"},
            {"indicator": "sales_income"},
            {"indicator": "sales_cost"},
            {"indicator": "other_income"},
            {"indicator": "manage_fee"},
            {"indicator": "deprec_and_amorti"},
            {"indicator": "total_other_opearting_expense"},
            {"indicator": "p_total_cost"},
            {"indicator": "operating_profit"},
            {"indicator": "total_gal"},
            {"indicator": "interest_income"},
            {"indicator": "interest_net_pay"},
            {"indicator": "interest_expense"},
            {"indicator": "income_from_asso_and_joint"},
            {"indicator": "other_gal_effct_profit_pre_tax"},
            {"indicator": "conti_op_before_tax"},
            {"indicator": "profit_before_noncurrent_items"},
            {"indicator": "profit_and_loss_of_noncurrent_items"},
            {"indicator": "profit_before_tax"},
            {"indicator": "income_tax"},
            {"indicator": "profit_after_tax"},
            {"indicator": "minoritygal"},
            {"indicator": "continue_operate_net_profit"},
            {"indicator": "noncontinue_operate_net_profit"},
            {"indicator": "other_special_items"},
            {"indicator": "ni_attr_to_cs"},
            {"indicator": "np_atms"},
            {"indicator": "preferred_divid_and_other_adjust"},
            {"indicator": "oci"},
            {"indicator": "total_oci"},
            {"indicator": "oci_from_parent"},
            {"indicator": "oci_from_minority"},
            {"indicator": "invest_property_fv_chg"},
            {"indicator": "operating_amt"},
            {"indicator": "oi_si"},
            {"indicator": "operating_premium_profit_si"},
            {"indicator": "to_toallied_corp_perf"},
            {"indicator": "to_joint_control_entity_perf"},
            {"indicator": "pre_tax_profit_si"},
            {"indicator": "after_tax_profit_si"},
            {"indicator": "profit_attrbt_to_nonholders"},
            {"indicator": "total_income_atncs"}
        ]
        
        df = self._fetch_financial_data_annual(symbol, indicators)
        if df.empty: return df
        self._save_raw_data(df, symbol, "income_statement_raw")

        rename_map = {
            'total_oi': 'revenue',
            'operating_amt': 'turnover', # Backup for revenue
            'gross_profit': 'gross_profit',
            'sales_ad_and_ga': 'sga_exp',
            'sales_fee': 'selling_marketing_exp',
            'manage_fee': 'ga_exp',
            'rad_cost': 'rd_exp',
            'income_tax': 'income_tax',
            'ni_attr_to_cs': 'net_income',
            'operating_profit': 'operating_profit',
            'depreciation': 'depreciation',
            'deprec_and_amorti': 'depreciation', # Backup
            'p_depreciation_and_amortization': 'depreciation' # Another backup
        }
        
        df_filtered = df.rename(columns=rename_map)
        
        # Calculate EBIT if not present but operating_profit is there
        if 'ebit' not in df_filtered.columns and 'operating_profit' in df_filtered.columns:
            # Simple approximation: Operating Profit is often used as EBIT
            df_filtered['ebit'] = df_filtered['operating_profit']

        for col in df_filtered.columns:
            if col not in ['date', 'end_date']:
                df_filtered[col] = pd.to_numeric(df_filtered[col], errors='coerce')
        
        return self._filter_data(df_filtered)

    def get_balance_sheet(self, symbol: str) -> pd.DataFrame:
        indicators = [
            {"indicator": "cce"},
            {"indicator": "st_investment"},
            {"indicator": "total_cash"},
            {"indicator": "account_receivable"},
            {"indicator": "tradable_fnncl_asset"},
            {"indicator": "derivative_fnncl_assets"},
            {"indicator": "restriv_fund"},
            {"indicator": "other_short_term_investment"},
            {"indicator": "ar_nr"},
            {"indicator": "total_ar"},
            {"indicator": "or"},
            {"indicator": "inventory"},
            {"indicator": "flow_assets_dit"},
            {"indicator": "pre_payment"},
            {"indicator": "other_cunrrent_assets_si"},
            {"indicator": "other_ca"},
            {"indicator": "total_ca"},
            {"indicator": "receivables_from_allied_corp"},
            {"indicator": "current_assets_si"},
            {"indicator": "prepay_deposits_etc"},
            {"indicator": "receivables_from_jce"},
            {"indicator": "receivables_from_ac"},
            {"indicator": "recoverable_tax"},
            {"indicator": "total_fixed_assets"},
            {"indicator": "depreciation"},
            {"indicator": "equity_and_lt_invest"},
            {"indicator": "net_fixed_assets"},
            {"indicator": "invest_property"},
            {"indicator": "equity_investment"},
            {"indicator": "investment_in_associate"},
            {"indicator": "investment_in_joints"},
            {"indicator": "held_to_maturity_invest"},
            {"indicator": "goodwill_and_intangible_asset"},
            {"indicator": "intangible_assets"},
            {"indicator": "accum_amortized"},
            {"indicator": "noncurrent_assets_dit"},
            {"indicator": "other_noncurrent_assets_si"},
            {"indicator": "dt_assets"},
            {"indicator": "total_noncurrent_assets"},
            {"indicator": "total_assets"},
            {"indicator": "ac_equity"},
            {"indicator": "lease_prepay"},
            {"indicator": "noncurrent_assets_si"},
            {"indicator": "st_lt_current_loan"},
            {"indicator": "trade_financial_lia"},
            {"indicator": "derivative_financial_lia"},
            {"indicator": "ap_np"},
            {"indicator": "accounts_payable"},
            {"indicator": "advance_payment"},
            {"indicator": "st_debt"},
            {"indicator": "contra_liab"},
            {"indicator": "tax_payable"},
            {"indicator": "accrued_liab"},
            {"indicator": "flow_debt_deferred_income"},
            {"indicator": "other_cl"},
            {"indicator": "other_cunrrent_liab_si"},
            {"indicator": "total_cl"},
            {"indicator": "accrued_expenses_etc"},
            {"indicator": "money_payable_toac"},
            {"indicator": "joint_control_entity_payable"},
            {"indicator": "payable_to_associated_corp"},
            {"indicator": "lt_debt"},
            {"indicator": "long_term_loan"},
            {"indicator": "other_noncurrent_liabi"},
            {"indicator": "deferred_tax_liability"},
            {"indicator": "ncl_deferred_income"},
            {"indicator": "other_noncurrent_liab_si"},
            {"indicator": "noncurrent_liab_si"},
            {"indicator": "total_noncurrent_liab"},
            {"indicator": "total_liab"},
            {"indicator": "common_shares"},
            {"indicator": "capital_reserve"},
            {"indicator": "equity_premium"},
            {"indicator": "treasury_stock"},
            {"indicator": "accumgal"},
            {"indicator": "equity_atsopc_sbi"},
            {"indicator": "preferred_stock"},
            {"indicator": "perpetual_debt"},
            {"indicator": "reserve"},
            {"indicator": "other_reserves"},
            {"indicator": "retained_earnings"},
            {"indicator": "oci_bs"},
            {"indicator": "total_common_equity"},
            {"indicator": "equity_belong_to_parent"},
            {"indicator": "minority_interests"},
            {"indicator": "other_equity_si"},
            {"indicator": "total_equity"},
            {"indicator": "total_lib_and_equity"},
            {"indicator": "equity_si"},
            {"indicator": "equity_atncs"}
        ]
        
        df = self._fetch_financial_data_annual(symbol, indicators)
        if df.empty: return df
        self._save_raw_data(df, symbol, "balance_sheet_raw")

        rename_map = {
            'cce': 'cash',
            'ar_nr': 'receivables',
            'inventory': 'inventory',
            'net_fixed_assets': 'fixed_assets',
            'equity_and_lt_invest': 'long_term_investments',
            'goodwill_and_intangible_asset': 'goodwill',
            'st_debt': 'short_term_debt',
            'st_lt_current_loan': 'short_term_borrowings',
            'ap_np': 'accounts_payable',
            'contra_liab': 'contract_liabilities',
            'advance_payment': 'advances_from_customers',
            'flow_debt_deferred_income': 'deferred_revenue',
            'lt_debt': 'long_term_debt',
            'long_term_loan': 'long_term_borrowings',
            'total_assets': 'total_assets',
            'equity_belong_to_parent': 'total_equity',
            'pre_payment': 'prepayment'
        }
        
        df_filtered = df.rename(columns=rename_map)
        
        # Deduplicate columns just in case
        df_filtered = df_filtered.loc[:, ~df_filtered.columns.duplicated()]

        if 'total_liabilities' not in df_filtered.columns or df_filtered['total_liabilities'].isnull().all():
            if 'total_liab' in df_filtered.columns:
                 df_filtered['total_liabilities'] = df_filtered['total_liab']
            elif 'total_assets' in df_filtered.columns and 'total_equity' in df_filtered.columns:
                df_filtered['total_liabilities'] = df_filtered['total_assets'] - df_filtered['total_equity']
                
        # Deduplicate again in case total_liabilities logic added a dupe (unlikely)
        df_filtered = df_filtered.loc[:, ~df_filtered.columns.duplicated()]

        for col in df_filtered.columns:
            if col not in ['date', 'end_date']:
                df_filtered[col] = pd.to_numeric(df_filtered[col], errors='coerce')
                
        return self._filter_data(df_filtered)

    def get_cash_flow(self, symbol: str) -> pd.DataFrame:
        indicators = [
            {"indicator": "ni"},
            {"indicator": "depreciation_and_amortization"},
            {"indicator": "operating_capital_change"},
            {"indicator": "ncf_from_oa"},
            {"indicator": "capital_cost"},
            {"indicator": "invest_buy"},
            {"indicator": "ncf_from_ia"},
            {"indicator": "increase_in_share_capital"},
            {"indicator": "decrease_in_share_capital"},
            {"indicator": "total_dividends_paid"},
            {"indicator": "ncf_from_fa"}
        ]
        
        df = self._fetch_financial_data_annual(symbol, indicators)
        if df.empty: return df
        self._save_raw_data(df, symbol, "cash_flow_raw")

        rename_map = {
            'ncf_from_oa': 'ocf',
            'capital_cost': 'capex',
            'total_dividends_paid': 'dividends'
        }
        
        df_filtered = df.rename(columns=rename_map)
        for col in df_filtered.columns:
            if col not in ['date', 'end_date']:
                df_filtered[col] = pd.to_numeric(df_filtered[col], errors='coerce')
        
        if 'capex' in df_filtered.columns:
            df_filtered['capex'] = df_filtered['capex'].abs()
            
        return self._filter_data(df_filtered)

    def get_market_metrics(self, symbol: str) -> dict:
        """获取公司基本信息（通过 ths_*_stock 基础指标）"""
        basic_info = self._fetch_basic_info(symbol)
        code = self._get_ifind_code(symbol)
        
        metrics = {
            "name": basic_info.get("name", ""),
            "list_date": basic_info.get("ipo_date", ""),
            "accounting_date": basic_info.get("accounting_date", ""),
            "acc_date": basic_info.get("acc_date", ""),
            "price": 0,
            "market_cap": 0,
            "pe": 0,
            "pb": 0,
            "dividend_yield": 0
        }
        
        # Fetch current market data using ths_* indicators confirmed for HK
        params = {
            "codes": code,
            "indipara": [
                {"indicator": "ths_close_price_stock", "indiparams": []},
                {"indicator": "ths_market_value_stock", "indiparams": []},
                {"indicator": "ths_pe_ttm_stock", "indiparams": []},
                {"indicator": "ths_pb_stock", "indiparams": []},
                {"indicator": "ths_dividend_ratio_stock", "indiparams": []}
            ]
        }
        res = self.cli.post("basic_data_service", params)
        df = self._parse_ifind_tables(res)
        if not df.empty:
            row = df.iloc[0]
            metrics["price"] = float(row.get("ths_close_price_stock") or 0)
            metrics["market_cap"] = float(row.get("ths_market_value_stock") or 0)
            metrics["pe"] = float(row.get("ths_pe_ttm_stock") or 0)
            metrics["pb"] = float(row.get("ths_pb_stock") or 0)
            metrics["dividend_yield"] = float(row.get("ths_dividend_ratio_stock") or 0)
            
        return metrics

    def get_historical_metrics(self, symbol: str, dates: list) -> pd.DataFrame:
        code = self._get_ifind_code(symbol)
        if not dates: return pd.DataFrame()
        
        results = []
        for d in dates:
            d_str = str(d).replace('-', '').replace('/', '')
            fmt_d = f"{d_str[:4]}-{d_str[4:6]}-{d_str[6:]}" if len(d_str) == 8 else d_str
            
            params = {
                "codes": code,
                "startdate": fmt_d,
                "enddate": fmt_d,
                "functionpara": {"Interval": "D", "Days": "Alldays", "Fill": "Previous"},
                "indipara": [
                    {"indicator": "pre_close", "indiparams": ["", "0", "CNY"]},
                    {"indicator": "market_value", "indiparams": ["", "CNY"]}
                ]
            }
            
            res = self.cli.post("date_sequence", params)
            df_seq = self._parse_ifind_tables(res)
            
            metrics = {'date_str': d_str, 'PE': 0.0, 'PB': 0.0, 'MarketCap': 0.0, 'Price': 0.0}
            
            if not df_seq.empty:
                match = df_seq[df_seq['end_date'] <= d_str].tail(1) if 'end_date' in df_seq.columns else df_seq.tail(1)
                if not match.empty:
                    if 'pre_close' in match.columns:
                        metrics['Price'] = float(match['pre_close'].iloc[0] or 0.0)
                    if 'market_value' in match.columns:
                        metrics['MarketCap'] = float(match['market_value'].iloc[0] or 0.0)
            results.append(metrics)
            
        df_hist = pd.DataFrame(results)
        self._save_raw_data(df_hist, symbol, "historical_metrics_raw")
        return df_hist

    def get_dividends(self, symbol: str) -> pd.DataFrame:
        code = self._get_ifind_code(symbol)
        current_year = int(time.strftime("%Y"))
        results = []
        
        for i in range(5):
            year_str = str(current_year - i)
            params = {
                "codes": code,
                "indipara": [
                    {"indicator": "annual_cum_dividend", "indiparams": [year_str, "CNY"]}
                ]
            }
            res = self.cli.post("basic_data_service", params)
            df = self._parse_ifind_tables(res)
            
            if not df.empty and 'annual_cum_dividend' in df.columns:
                val = df['annual_cum_dividend'].iloc[0]
                if pd.notna(val) and val != 0:
                    results.append({
                        'date_str': f"{year_str}1231", # Assume yearend for dividends
                        'dividends': float(val)
                    })
        
        if not results:
            return pd.DataFrame()
            
        df_div = pd.DataFrame(results)
        self._save_raw_data(df_div, symbol, "dividends_raw")
        return df_div

    def get_repurchases(self, symbol: str) -> pd.DataFrame:
        code = self._get_ifind_code(symbol)
        current_year = int(time.strftime("%Y"))
        results = []
        
        for i in range(5):
            target_year = current_year - i
            start_date = f"{target_year - 1}-12-31"
            end_date = f"{target_year}-12-31"
            
            params = {
                "codes": code,
                "indipara": [
                    {"indicator": "repur_num_new", "indiparams": [start_date, end_date, "1"]}
                ]
            }
            res = self.cli.post("basic_data_service", params)
            df = self._parse_ifind_tables(res)
            
            if not df.empty and 'repur_num_new' in df.columns:
                val = df['repur_num_new'].iloc[0]
                if pd.notna(val) and val != 0:
                    results.append({
                        'date_str': f"{target_year}1231",
                        'repurchases': float(val)
                    })
        
        if not results:
            return pd.DataFrame()
            
        df_repur = pd.DataFrame(results)
        self._save_raw_data(df_repur, symbol, "repurchases_raw")
        return df_repur

    def get_employee_count(self, symbol: str) -> pd.DataFrame:
        code = self._get_ifind_code(symbol)
        current_year = int(time.strftime("%Y"))
        results = []
        
        for i in range(5):
            target_year = current_year - i
            target_date = f"{target_year}-12-31"
            
            params = {
                "codes": code,
                "indipara": [
                    {"indicator": "staff_num", "indiparams": [target_date]}
                ]
            }
            res = self.cli.post("basic_data_service", params)
            df = self._parse_ifind_tables(res)
            
            if not df.empty and 'staff_num' in df.columns:
                val = df['staff_num'].iloc[0]
                if pd.notna(val) and val != 0:
                    results.append({
                        'date_str': f"{target_year}1231",
                        'employee_count': float(val)
                    })
        
        if not results:
            return pd.DataFrame()
            
        df_emp = pd.DataFrame(results)
        self._save_raw_data(df_emp, symbol, "employee_count_raw")
        return df_emp

    def get_financial_ratios(self, symbol: str) -> pd.DataFrame:
        """获取官方计算的财务指标（比率、周转天数等）"""
        code = self._get_ifind_code(symbol)
        current_year = int(time.strftime("%Y"))
        
        # 1. Determine the latest valid year
        last_valid_year = None
        for offset in range(3):
            test_year = current_year - offset
            # Try getting ROE as a proxy for data availability
            test_date = f"{test_year}1231"
            params = {
                "codes": code,
                "indipara": [{"indicator": "roe", "indiparams": [test_date]}]
            }
            res = self.cli.post("basic_data_service", params)
            df = self._parse_ifind_tables(res)
            
            if not df.empty:
                val = df.iloc[0, 0] if not df.empty and df.shape[1] > 0 else None
                if pd.notna(val) and val != 0:
                    last_valid_year = test_year
                    break
                    
        if last_valid_year is None:
            last_valid_year = current_year

        all_dfs = []
        
        # 2. Fetch 5 years starting from last valid year
        for i in range(5):
            target_year = last_valid_year - i
            date_str = f"{target_year}1231"
            year_str = str(target_year)
            
            indipara = []
            
            # 1. 人均指标 (参数: Year, "100")
            for key in ["salary_pp", "revenue_pp", "profit_pp"]:
                indipara.append({"indicator": key, "indiparams": [year_str, "100"]})
                
            # 2. 财务比率与周转率 (参数: Date YYYYMMDD)
            ratio_keys = [
                "roe", "roa", "roic", 
                "sales_fee_to_or", "manage_fee_to_revenue", "rad_expense_to_total_income",
                "operating_revenue_yoy", "np_atsopc_yoy", 
                "ibdebt_ratio_asset_base", 
                "inventory_turnover_days", "receivable_turnover_days", "accounts_payable_turnover_days",
                "fixed_asset_turnover_ratio", "total_capital_turnover"
            ]
            for key in ratio_keys:
                indipara.append({"indicator": key, "indiparams": [date_str]})
                
            params = {
                "codes": code,
                "indipara": indipara
            }
            
            res = self.cli.post("basic_data_service", params)
            df = self._parse_ifind_tables(res)
            
            if not df.empty:
                if 'end_date' not in df.columns:
                    df['end_date'] = date_str
                
                # Filter out columns that are all NaN
                df = df.dropna(axis=1, how='all')
                
                # Identify if we have meaningful data (at least one valid metric)
                valid_cols = [c for c in df.columns if c not in ['end_date', 'date', 'code', 'thscode']]
                if not df[valid_cols].isnull().all().all():
                     all_dfs.append(df)
                    
        if not all_dfs:
            return pd.DataFrame()
            
        combined = pd.concat(all_dfs, ignore_index=True)
        self._save_raw_data(combined, symbol, "financial_ratios_raw")
        
        rename_map = {
             "salary_pp": "salary_per_employee",
             "revenue_pp": "revenue_per_employee",
             "profit_pp": "profit_per_employee",
             "sales_fee_to_or": "selling_expense_ratio",
             "manage_fee_to_revenue": "admin_expense_ratio",
             "rad_expense_to_total_income": "rd_expense_ratio",
             "operating_revenue_yoy": "revenue_growth",
             "np_atsopc_yoy": "net_profit_growth",
             "ibdebt_ratio_asset_base": "interest_bearing_debt_ratio",
             "fixed_asset_turnover_ratio": "fixed_asset_turnover",
             "total_capital_turnover": "total_asset_turnover"
        }
        
        df_final = combined.rename(columns=rename_map)
        
        for col in df_final.columns:
             if col not in ['date', 'end_date']:
                 df_final[col] = pd.to_numeric(df_final[col], errors='coerce')

        return self._filter_data(df_final)
