import pandas as pd
from django.http import HttpResponse
from rest_framework.response import Response
try:
    from .config import TS_TOKEN, START_DATE, END_DATE
except (ImportError, SystemError):
    from config import TS_TOKEN, START_DATE, END_DATE

from .data_source import get_tushare_pro

# 统一入口（全局单例，向后兼容旧代码直接访问 pro）
pro = get_tushare_pro()

def dataCorrect(df, columns=None):
    """
    检查并修正DataFrame中的NaN或None值。
    1. 如果前后有数字,该字段取前后值的均值填入
    2. 如果仅前或后有数字,该字段取copy前或后数字
    3. 若前后都为NaN,则设为0
    4. 若出发点修改,打印修改情况到屏幕

    Parameters:
        df (pd.DataFrame): 要处理的数据框
        columns (list): 要检查的列名列表,如果为None则检查所有列
    """
    # 如果没有指定列,则检查所有列
    if columns is None:
        columns = df.columns
    # 遍历指定列
    for col in columns:
        # 遍历每一行
        for i in range(len(df)):
            # 检查当前值是否为NaN或None
            if pd.isna(df.at[i, col]):
                # 获取前后值
                prev_val = df.at[i-1, col] if i > 0 else None
                next_val = df.at[i+1, col] if i < len(df) - 1 else None

                # 检查前后值是否为有效数字
                prev_valid = prev_val is not None and not pd.isna(prev_val)
                next_valid = next_val is not None and not pd.isna(next_val)
                # 如果前后都有值,取均值
                if prev_valid and next_valid:
                    new_val = (prev_val + next_val) / 2
                    df.at[i, col] = new_val
                    #print(f"修正 {col} 列第 {i} 行: 前后值均值填充为 {new_val}")

                # 如果只有前值,取前值
                elif prev_valid:
                    df.at[i, col] = prev_val
                    #print(f"修正 {col} 列第 {i} 行: 前值填充为 {prev_val}")

                # 如果只有后值,取后值
                elif next_valid:
                    df.at[i, col] = next_val
                    #print(f"修正 {col} 列第 {i} 行: 后值填充为 {next_val}")

                # 如果前后都没有有效值,设为0
                else:
                    df.at[i, col] = 0
                    #print(f"修正 {col} 列第 {i} 行: 前后均无有效值,设为0")

    return df

def dataMerge(*dfs):
    """
    合并多个股票数据DataFrame，确保合并后的数据ts_code和trade_date匹配。

    Parameters:
        *dfs: 可变数量的DataFrame参数，每个DataFrame应包含ts_code和trade_date列

    Returns:
        pd.DataFrame: 合并后的DataFrame，包含所有输入DataFrame的列。如果某行的ts_code或trade_date不匹配，则用NaN填充。
    """
    if len(dfs) < 2:
        raise ValueError("至少需要提供2个DataFrame进行合并")

    # 检查所有DataFrame是否都有ts_code和trade_date列
    for df in dfs:
        if 'ts_code' not in df.columns or 'trade_date' not in df.columns:
            raise ValueError("所有DataFrame必须包含ts_code和trade_date列")

    # 初始化合并结果为第一个DataFrame
    merged_df = dfs[0].copy()

    # 逐个合并剩余的DataFrame
    for df in dfs[1:]:
        merged_df = pd.merge(merged_df, df, on=['ts_code', 'trade_date'], how='outer')

    return merged_df

def tscodeCheck(tscode):
    """
    检查并修正股票代码格式。

    Parameters:
        tscode (str): 股票代码

    Returns:
        str: 格式正确的股票代码 (如 '000001.SZ')

    Raises:
        ValueError: 如果输入不符合要求
    """
    if not isinstance(tscode, str):
        raise ValueError("输入必须是字符串")

    tscode = tscode.upper()  # 转为大写

    # 检查长度
    if len(tscode) < 6:
        raise ValueError("股票代码长度不能小于6位")
    elif len(tscode) == 6:
        if not tscode.isdigit():
            raise ValueError("6位股票代码必须全为数字")
        # 根据开头添加后缀
        if tscode.startswith('00') or tscode.startswith('3'):
            return f"{tscode}.SZ"
        elif tscode.startswith(('60','68')): # 68为科创板
            return f"{tscode}.SH"
        elif tscode.startswith(('8', '9')):
            return f"{tscode}.BJ"
        else:
            raise ValueError("未知的6位股票代码开头")
    elif len(tscode) == 9:
        prefix = tscode[:6]
        suffix = tscode[-3:]
        if not prefix.isdigit():
            raise ValueError("9位股票代码前6位必须为数字")
        if suffix not in ('.SZ', '.SH', '.BJ'):
            raise ValueError("9位股票代码后缀必须是.SZ/.SH/.BJ")
        return tscode
    else:
        raise ValueError("股票代码长度必须为6位或9位")



def get_trading_dates(trade_date):
    """
    获取上一个季度财报日期到给定日期的所有交易日期

    Parameters:
        trade_date (str): 给定日期 (格式yyyymmdd)
    Returns:
        list: 交易日期列表 (格式yyyymmdd)
    """
    # 将输入日期转为datetime
    try:
        current_date = pd.to_datetime(trade_date, format='%Y%m%d')
    except:
        raise ValueError("trade_date格式应为yyyymmdd")

    # 计算上一个季度财报日期 (3/31, 6/30, 9/30, 12/31)
    year = current_date.year
    month = current_date.month
    if month < 4:
        prev_report_date = pd.Timestamp(year-1, 12, 31)
    elif month < 7:
        prev_report_date = pd.Timestamp(year, 3, 31)
    elif month < 10:
        prev_report_date = pd.Timestamp(year, 6, 30)
    else:
        prev_report_date = pd.Timestamp(year, 9, 30)

    # 获取交易日历
    calendar_df = pro.trade_cal(exchange='', start_date=prev_report_date.strftime('%Y%m%d'), end_date=trade_date)

    # 过滤交易日历
    calendar_df['cal_date_dt'] = pd.to_datetime(calendar_df['cal_date'], format='%Y%m%d')
    filtered_dates = calendar_df[
        (calendar_df['cal_date_dt'] > prev_report_date) &
        (calendar_df['cal_date_dt'] <= current_date) &
        (calendar_df['is_open'] == 1)
    ]

    # 返回日期列表 (格式yyyymmdd)
    return filtered_dates['cal_date'].tolist()

def get_next_report_date(trade_date):
    """
    获取给定日期的下一个财报日期

    Parameters:
        trade_date (str): 给定日期 (格式yyyymmdd)

    Returns:
        str: 下一个财报日期 (格式yyyymmdd)
    """
    try:
        current_date = pd.to_datetime(trade_date, format='%Y%m%d')
    except:
        raise ValueError("trade_date格式应为yyyymmdd")

    year = current_date.year
    month = current_date.month
    day = current_date.day

    if month < 3 or (month == 3 and day < 31):
        return f"{year}0331"
    elif month < 6 or (month == 6 and day < 30):
        return f"{year}0630"
    elif month < 9 or (month == 9 and day < 30):
        return f"{year}0930"
    elif month < 12 or (month == 12 and day < 31):
        return f"{year}1231"
    else:
        return f"{year+1}0331"
    
def viewFunc_singleParam(request, data_func, param_name='tscode', default_value=None):
    """
    通用单参数视图包装器。

    :param request: Django request 对象
    :param data_func: 接收单个参数的数据处理函数
    :param param_name: URL 查询参数名
    :param default_value: 参数默认值
    :return: Response
    """
    param = request.GET.get(param_name, default_value)
    if not param:
        return Response({'error': f'缺少 {param_name} 参数'}, status=400)

    try:
        data = data_func(param)
        dict_data = data.to_dict(orient='records') if isinstance(data, pd.DataFrame) else data
        return Response(dict_data)
    except ImportError:
        return Response({'error': f'模块不存在'}, status=500)
    except Exception as e:
        return Response({'error': str(e)}, status=500)


def viewFunc_tsCodeAndDate(request,data_func):
    """
    通用数据处理函数
    :param request: Django request对象
    :param data_func: 数据处理函数（需返回Response）
    :return: Response
    """
    #通过url 传入 tscode 变量
    tscode = request.GET.get('tscode','000001.SZ')  # 从URL获取industry参数
    tscode = tscodeCheck(tscode)
    if not tscode:
        return Response({'error': '缺少 tscode 参数'}, status=400)

    start_date = request.GET.get('start_date', START_DATE)
    end_date = request.GET.get('end_date', END_DATE)

    try:
        data = data_func(tscode, start_date, end_date)  # 调用函数
        dict_data = data.to_dict(orient='records') if isinstance(data, pd.DataFrame) else data
        return Response(dict_data)
    except ImportError:
        return Response({'error': '模块 stock_basic 不存在'}, status=500)
    except Exception as e:
        return Response({'error': str(e)}, status=500)

def date_format_correction(date_str):
    """
    日期格式矫正函数：
    1. 如果输入格式为yyyy-mm-dd则返回yyyymmdd格式
    2. 如果输入格式已经是yyyymmdd则直接返回
    3. 其他情况返回None

    Parameters:
        date_str (str): 日期字符串

    Returns:
        str: yyyymmdd格式的日期字符串，如果输入格式不正确则返回None
    """
    try:
        # 先尝试解析yyyymmdd格式
        pd.to_datetime(date_str, format='%Y%m%d')
        return date_str
    except ValueError:
        try:
            # 再尝试解析yyyy-mm-dd格式
            date_obj = pd.to_datetime(date_str, format='%Y-%m-%d')
            return date_obj.strftime('%Y%m%d')
        except ValueError:
            return None


from datetime import datetime

def get_released_report_dates(start_date: str, end_date: str) -> list:
    """
    根据起止日期生成所有财报季末日，并去除尚未可能披露的日期。

    :param start_date: 开始日期，格式为 "YYYYMMDD"
    :param end_date: 结束日期，格式为 "YYYYMMDD"
    :param today: 当前日期，格式为 "YYYYMMDD"，默认为系统当前日期
    :return: 已披露的财报季末日期列表，格式为 ["20240331", "20240630", ...]
    """
  
    today = datetime.today()

    # 财报发布日期最晚规则
    report_deadlines = {
        "0331": "0430",
        "0630": "0831",
        "0930": "1031",
        "1231": "0430"  # 次年4月30日
    }

    # 生成所有财报季度末日期
    years = range(int(start_date[:4]), int(end_date[:4]) + 1)
    report_dates = [
        f"{year}{quarter}"
        for year in years
        for quarter in ["0331", "0630", "0930", "1231"]
    ]

    # 筛选日期范围内的
    report_dates = [d for d in report_dates if start_date <= d <= end_date]

    def is_report_released(report_date_str):
        report_dt = datetime.strptime(report_date_str, "%Y%m%d")
        year = report_dt.year
        q_end = report_date_str[4:]

        if q_end == "1231":
            deadline = datetime.strptime(f"{year+1}0430", "%Y%m%d")
        else:
            deadline = datetime.strptime(f"{year}{report_deadlines[q_end]}", "%Y%m%d")

        return today >= deadline

    # 返回已发布的日期
    return [d for d in report_dates if is_report_released(d)]

'''
写一个函数，调用stock_basic API(说明文档：https://tushare.pro/document/2?doc_id=25) 获取个股清单
给定交易所代码，仅查询当前仍然上市的股票
返回：
    - ts_code: TS代码
    - symbol: 股票代码
    - name: 股票名称
    - fullname: 股票全称
    - exchange: 交易所代码 SSE上交所 SZSE深交所 BSE北交所
    - list_status: 上市状态 L上市 D退市 P暂停上市
'''
def get_stock_basic(exchange='SSE'):
    """
    获取指定交易所的上市股票基本信息

    Parameters:
        exchange (str): 交易所代码 SSE上交所 SZSE深交所 BSE北交所

    Returns:
        pd.DataFrame: 包含股票基本信息的DataFrame
    """
    # 调用stock_basic接口
    df = pro.stock_basic(exchange=exchange, list_status='L',
                        fields='ts_code,symbol,name,fullname,exchange,list_status')

    return df

if __name__ == "__main__":
    # 测试当前日期之前的财报
    print("\n测试2: 当前日期之前的财报")
    result = get_released_report_dates("20230101", "20251230")
    print(f"结果: {result}")
