# -*- coding: utf-8 -*-
"""
DataTable 및 AnimMontage 데이터 추출 모듈
"""

import logging
from typing import Dict, List, Optional, Any
from custom_property_parser import extract_notify_properties, parse_custom_property

logger = logging.getLogger(__name__)


# 수집 대상 스토커 목록 (소문자)
TARGET_STALKERS = [
    'hilda', 'urud', 'nave', 'baran', 'rio', 'clad',
    'rene', 'sinobu', 'lian', 'cazimord', 'blackmaria'
]


def round_float(value: Any) -> Any:
    """소수점 2자리로 반올림 (float만 처리)"""
    if isinstance(value, (int, float)):
        return round(float(value), 2)
    return value


def extract_character_stat(data_table_assets: List[Dict], stalker_name: str) -> Dict:
    """
    DT_CharacterStat에서 스토커의 기본 스탯 추출

    Args:
        data_table_assets: DataTable.json의 Assets 배열
        stalker_name: 스토커 이름 (소문자)

    Returns:
        스탯 딕셔너리
    """
    # DT_CharacterStat 찾기
    dt_char_stat = None
    for asset in data_table_assets:
        if asset.get('AssetName') == 'DT_CharacterStat':
            dt_char_stat = asset
            break

    if not dt_char_stat:
        logger.warning("DT_CharacterStat not found")
        return {}

    # 해당 스토커 행 찾기
    rows = dt_char_stat.get('Rows', [])
    stalker_row = None
    for row in rows:
        if row.get('RowName', '').lower() == stalker_name.lower():
            stalker_row = row
            break

    if not stalker_row:
        logger.warning(f"Stalker '{stalker_name}' not found in DT_CharacterStat")
        return {}

    data = stalker_row.get('Data', {})

    # 모든 숫자 필드를 소수점 2자리로 반올림
    stats = {
        'name': data.get('name', ''),
        'jobName': data.get('jobName', ''),
        # 기본 스탯
        'str': round_float(data.get('str', 0)),
        'dex': round_float(data.get('dex', 0)),
        'int': round_float(data.get('int', 0)),
        'con': round_float(data.get('con', 0)),
        'wis': round_float(data.get('wis', 0)),
        # 체력/마나
        'hp': round_float(data.get('hP', 0)),
        'mp': round_float(data.get('mP', 0)),
        'manaRegen': round_float(data.get('manaRegen', 0)),
        'stamina': round_float(data.get('stamina', 0)),
        # 공격
        'physicalDamage': round_float(data.get('physicalDamage', 0)),
        'magicalDamage': round_float(data.get('magicalDamage', 0)),
        'criticalPer': round_float(data.get('criticalPer', 0)),
        'criticalDamage': round_float(data.get('criticalDamage', 0)),
        'backAttackDamage': round_float(data.get('backAttackDamage', 0)),
        # 방어
        'defense': round_float(data.get('defense', 0)),
        'physicalResistancePer': round_float(data.get('physicalResistancePer', 0)),
        'rangedResistancePer': round_float(data.get('rangedResistancePer', 0)),
        'magicalResistancePer': round_float(data.get('magicalResistancePer', 0)),
        # 속성 저항
        'fireResistancePer': round_float(data.get('fireResistancePer', 0)),
        'poisonResistancePer': round_float(data.get('poisonResistancePer', 0)),
        'waterResistancePer': round_float(data.get('waterResistancePer', 0)),
        'lightningResistancePer': round_float(data.get('lightningResistancePer', 0)),
        'holyResistancePer': round_float(data.get('holyResistancePer', 0)),
        'darkResistancePer': round_float(data.get('darkResistancePer', 0)),
        'dotReduceRatePer': round_float(data.get('dOTReduceRatePer', 0)),
        # 이동
        'walkSpeed': round_float(data.get('walkSpeed', 0)),
        # 스킬
        'defaultSkills': data.get('defaultSkills', []),
        'subSkill': data.get('subSkill', ''),
        'ultimateSkill': data.get('ultimateSkill', ''),
        'ultimatePoint': data.get('ultimatePoint', 0),
        # 장비
        'equipableTypes': data.get('equipableTypes', []),
        # 기타
        'hitRadius': round_float(data.get('hitRadius', 0))
    }

    return stats


def extract_attack_montages(data_table_assets: List[Dict], stalker_name: str) -> Dict[str, List[str]]:
    """
    DT_CharacterAbility에서 기본 공격 몽타주 추출

    Args:
        data_table_assets: DataTable.json의 Assets 배열
        stalker_name: 스토커 이름 (소문자)

    Returns:
        무기타입별 몽타주 경로 리스트 딕셔너리
    """
    # DT_CharacterAbility 찾기
    dt_char_ability = None
    for asset in data_table_assets:
        if asset.get('AssetName') == 'DT_CharacterAbility':
            dt_char_ability = asset
            break

    if not dt_char_ability:
        logger.warning("DT_CharacterAbility not found")
        return {}

    # 해당 스토커 행 찾기
    rows = dt_char_ability.get('Rows', [])
    stalker_row = None
    for row in rows:
        if row.get('RowName', '').lower() == stalker_name.lower():
            stalker_row = row
            break

    if not stalker_row:
        logger.warning(f"Stalker '{stalker_name}' not found in DT_CharacterAbility")
        return {}

    data = stalker_row.get('Data', {})
    attack_montage_map = data.get('attackMontageMap', {})

    # 무기타입별 몽타주 배열 추출
    result = {}
    for weapon_type, weapon_data in attack_montage_map.items():
        if isinstance(weapon_data, dict):
            montage_array = weapon_data.get('montageArray', [])
            if montage_array:
                result[weapon_type] = montage_array

    return result


def should_exclude_skill_montage(montage_path: str) -> bool:
    """
    스킬 몽타주 제외 규칙 적용

    제외 키워드: ready, Equip, Equipment, _E
    """
    if not montage_path:
        return True

    exclude_keywords = ['ready', 'Equip', 'Equipment', '_E']
    montage_name = montage_path.split('/')[-1].split('.')[0]

    for keyword in exclude_keywords:
        if keyword in montage_name:
            return True

    return False


def extract_skill_data(data_table_assets: List[Dict], skill_id: str) -> Optional[Dict]:
    """
    DT_Skill에서 스킬 데이터 추출

    Args:
        data_table_assets: DataTable.json의 Assets 배열
        skill_id: 스킬 ID (예: SK100201)

    Returns:
        스킬 데이터 딕셔너리
    """
    # DT_Skill 찾기
    dt_skill = None
    for asset in data_table_assets:
        if asset.get('AssetName') == 'DT_Skill':
            dt_skill = asset
            break

    if not dt_skill:
        logger.warning("DT_Skill not found")
        return None

    # 해당 스킬 행 찾기
    rows = dt_skill.get('Rows', [])
    skill_row = None
    for row in rows:
        if row.get('RowName') == skill_id:
            skill_row = row
            break

    if not skill_row:
        logger.warning(f"Skill '{skill_id}' not found in DT_Skill")
        return None

    data = skill_row.get('Data', {})

    # 스킬 몽타주 필터링 (제외 규칙 적용)
    use_montages = data.get('useMontages', [])
    filtered_montages = [m for m in use_montages if not should_exclude_skill_montage(m)]

    # GameplayEffect 정보 추출 (trigger와 gEClass만)
    gameplay_effects = []
    for ge in data.get('gameplayEffectSet', []):
        if isinstance(ge, dict):
            gameplay_effects.append({
                'trigger': ge.get('trigger', ''),
                'gEClass': ge.get('gEClass', '')
            })

    skill_data = {
        'skillId': skill_id,
        'name': data.get('name', ''),
        'desc': data.get('desc', ''),
        'descValues': data.get('descValues', []),
        'simpleDesc': data.get('simpleDesc', ''),
        'skillAttackType': data.get('skillAttackType', ''),
        'skillElementType': data.get('skillElementType', ''),
        'skillDamageRate': data.get('skillDamageRate', 1.0),
        'walkSpeedMultiplier': data.get('walkSpeedMultiplier', 0),
        'castingTime': data.get('castingTime', 0),
        'manaCost': data.get('manaCost', 0),
        'coolTime': data.get('coolTime', 0),
        'useMontages': filtered_montages,
        'bIsStackable': data.get('bIsStackable', False),
        'maxStackCount': data.get('maxStackCount', 0),
        'bIsUltimate': data.get('bIsUltimate', False),
        'abilityClass': data.get('abilityClass', ''),
        'activeAbilityClass': data.get('activeAbilityClass', ''),
        'activeDuration': data.get('activeDuration', 0),
        'gameplayEffectSet': gameplay_effects
    }

    return skill_data


def extract_montage_asset_name(montage_path: str) -> str:
    """
    몽타주 경로에서 에셋 이름 추출

    Example:
        '/Script/Engine.AnimMontage'/Game/.../AM_PC_Hilda_B_Attack_W01_01.AM_PC_Hilda_B_Attack_W01_01''
        -> 'AM_PC_Hilda_B_Attack_W01_01'
    """
    if not montage_path:
        return ''

    # 마지막 .과 마지막 ' 사이의 문자열 추출
    parts = montage_path.split('.')
    if len(parts) >= 2:
        asset_name = parts[-1].rstrip("'")
        return asset_name

    return montage_path


def extract_anim_notifies(montage_data: Dict) -> List[Dict]:
    """
    AnimMontage에서 주요 AnimNotifies 추출

    수집 대상:
    - ANS_AttackState_C
    - AnimNotifyState_AttackWithEquip
    - ANS_SkillCancel_C
    - AN_Trigger_Projectile_Shot_C

    Args:
        montage_data: AnimMontage 에셋 딕셔너리

    Returns:
        추출된 AnimNotifies 리스트
    """
    target_classes = {
        'ANS_AttackState_C': ['AddNormalAttackPer', 'AddPhysicalAttackPer'],
        'AnimNotifyState_AttackWithEquip': ['AttackTag'],
        'ANS_SkillCancel_C': [],
        'AN_Trigger_Projectile_Shot_C': ['EventTag']
    }

    anim_notifies = montage_data.get('AnimNotifies', [])
    result = []

    for notify in anim_notifies:
        notify_class = notify.get('NotifyStateClass') or notify.get('NotifyClass')

        if notify_class in target_classes:
            extracted = {
                'notifyClass': notify_class,
                'triggerTime': notify.get('TriggerTime', 0),
                'duration': notify.get('Duration', 0)
            }

            # CustomProperties에서 필요한 필드 추출
            target_fields = target_classes[notify_class]
            if target_fields:
                props = extract_notify_properties(notify, target_fields)
                extracted['properties'] = props

            result.append(extracted)

    return result


def find_montage_data(anim_montage_assets: List[Dict], montage_path: str) -> Optional[Dict]:
    """
    몽타주 경로로 AnimMontage 에셋 찾기

    Args:
        anim_montage_assets: AnimMontage.json의 Assets 배열
        montage_path: 몽타주 경로

    Returns:
        찾은 몽타주 데이터 또는 None
    """
    asset_name = extract_montage_asset_name(montage_path)
    if not asset_name:
        return None

    for montage in anim_montage_assets:
        if montage.get('AssetName') == asset_name:
            return montage

    return None


def extract_montage_info(anim_montage_assets: List[Dict], montage_path: str) -> Optional[Dict]:
    """
    몽타주 경로로 몽타주 정보 추출 (AnimNotifies 포함)

    Args:
        anim_montage_assets: AnimMontage.json의 Assets 배열
        montage_path: 몽타주 경로

    Returns:
        몽타주 정보 딕셔너리
    """
    montage_data = find_montage_data(anim_montage_assets, montage_path)
    if not montage_data:
        asset_name = extract_montage_asset_name(montage_path)
        logger.warning(f"Montage not found: {asset_name}")
        return None

    sections = montage_data.get('Sections', [])
    section_info = []
    for section in sections:
        section_info.append({
            'sectionName': section.get('SectionName', ''),
            'startTime': section.get('StartTime', 0)
        })

    montage_info = {
        'assetName': montage_data.get('AssetName', ''),
        'sequenceLength': montage_data.get('SequenceLength', 0),
        'rateScale': montage_data.get('RateScale', 1.0),
        'sections': section_info,
        'animNotifies': extract_anim_notifies(montage_data)
    }

    return montage_info
