use bladeink::story::Story;
use glam::Vec3;

use crate::components::dialog::{
    DialogBubbleComponent, DialogPhase, DialogProjectileComponent, ParryButton,
};
use crate::components::trigger::TriggerEventKind;
use crate::entity::EntityHandle;
use crate::world::{Transform, World};

const DEFAULT_DISPLAY_TIME: f32 = 3.0;
const PARRY_TAG_PREFIX: &str = "parry:";
const TIMER_TAG_PREFIX: &str = "timer:";

pub fn dialog_system(world: &mut World, delta: f32)
{
    process_trigger_events(world);
    tick_displaying_bubbles(world, delta);
    process_outcomes(world);
}

fn process_trigger_events(world: &mut World)
{
    let events: Vec<_> = world.trigger_events.iter().cloned().collect();

    for event in events
    {
        let has_source = world.dialog_sources.get(event.trigger_entity).is_some();
        if !has_source
        {
            continue;
        }

        match event.kind
        {
            TriggerEventKind::Entered =>
            {
                let already_active = world.bubble_tags.all().iter().any(|&b| {
                    world
                        .dialog_bubbles
                        .with(b, |db| db.character_entity == event.trigger_entity)
                        .unwrap_or(false)
                });

                if already_active
                {
                    continue;
                }

                spawn_bubble(world, event.trigger_entity);
            }

            TriggerEventKind::Exited =>
            {
                despawn_bubbles_for_character(world, event.trigger_entity);
            }
        }
    }
}

fn spawn_bubble(world: &mut World, character_entity: EntityHandle)
{
    let ink_json = match world
        .dialog_sources
        .with(character_entity, |s| s.ink_json.clone())
    {
        Some(json) => json,
        None => return,
    };

    let mut story = match Story::new(&ink_json)
    {
        Ok(s) => s,
        Err(e) =>
        {
            eprintln!("Failed to load ink story: {e}");
            return;
        }
    };

    let (text, parry, display_time) = advance_story(&mut story);

    let character_pos = world
        .transforms
        .with(character_entity, |t| t.position)
        .unwrap_or(Vec3::ZERO);

    let bubble_entity = world.spawn();
    world.transforms.insert(
        bubble_entity,
        Transform::from_position(character_pos + Vec3::new(0.0, 2.5, 0.0)),
    );
    world
        .names
        .insert(bubble_entity, "DialogBubble".to_string());
    world.bubble_tags.insert(bubble_entity, ());
    world.dialog_bubbles.insert(
        bubble_entity,
        DialogBubbleComponent {
            story,
            character_entity,
            current_text: text,
            phase: DialogPhase::Displaying {
                timer: display_time,
            },
            correct_parry: parry,
            display_time,
        },
    );

    println!("Dialog bubble spawned for character {character_entity}");
}

fn despawn_bubbles_for_character(world: &mut World, character_entity: EntityHandle)
{
    let bubbles: Vec<EntityHandle> = world.bubble_tags.all();

    for bubble_entity in bubbles
    {
        let matches = world
            .dialog_bubbles
            .with(bubble_entity, |b| b.character_entity == character_entity)
            .unwrap_or(false);

        if !matches
        {
            continue;
        }

        if let Some(bubble) = world.dialog_bubbles.get(bubble_entity)
        {
            if let DialogPhase::ProjectileInFlight { projectile_entity } = bubble.phase
            {
                world.despawn(projectile_entity);
            }
        }

        world.despawn(bubble_entity);
        println!("Dialog bubble despawned for character {character_entity}");
    }
}

fn tick_displaying_bubbles(world: &mut World, delta: f32)
{
    let bubbles: Vec<EntityHandle> = world.bubble_tags.all();

    for bubble_entity in bubbles
    {
        let character_entity = match world.dialog_bubbles.with(bubble_entity, |b| {
            if matches!(b.phase, DialogPhase::Displaying { .. })
            {
                Some(b.character_entity)
            }
            else
            {
                None
            }
        })
        {
            Some(Some(e)) => e,
            _ => continue,
        };

        let expired = world
            .dialog_bubbles
            .with_mut(bubble_entity, |b| {
                if let DialogPhase::Displaying { ref mut timer } = b.phase
                {
                    *timer -= delta;
                    *timer <= 0.0
                }
                else
                {
                    false
                }
            })
            .unwrap_or(false);

        if expired
        {
            let correct_parry = match world
                .dialog_bubbles
                .with(bubble_entity, |b| b.correct_parry)
            {
                Some(Some(p)) => p,
                _ =>
                {
                    world.despawn(bubble_entity);
                    continue;
                }
            };

            let character_pos = world
                .transforms
                .with(character_entity, |t| t.position)
                .unwrap_or(Vec3::ZERO);

            let projectile_entity = world.spawn();
            world.transforms.insert(
                projectile_entity,
                Transform::from_position(character_pos + Vec3::new(0.0, 1.5, 0.0)),
            );
            world
                .names
                .insert(projectile_entity, "DialogProjectile".to_string());
            world.projectile_tags.insert(projectile_entity, ());
            world.dialog_projectiles.insert(
                projectile_entity,
                DialogProjectileComponent {
                    bubble_entity,
                    correct_parry,
                    parry_window_open: false,
                },
            );

            world.dialog_bubbles.with_mut(bubble_entity, |b| {
                b.phase = DialogPhase::ProjectileInFlight { projectile_entity };
            });

            println!(
                "Dialog projectile spawned, correct parry: {:?}",
                correct_parry
            );
        }
    }
}

fn process_outcomes(world: &mut World)
{
    let outcomes: Vec<_> = world.dialog_outcomes.drain(..).collect();

    for event in outcomes
    {
        let bubble_entity = event.bubble_entity;

        if world.dialog_bubbles.get(bubble_entity).is_none()
        {
            continue;
        }

        let choice_tag = event.outcome.to_choice_tag();

        let next = world.dialog_bubbles.with_mut(bubble_entity, |b| {
            let choices = b.story.get_current_choices();
            let idx = choices
                .iter()
                .position(|c| c.tags.iter().any(|t| t.trim() == choice_tag));

            if let Some(idx) = idx
            {
                let _ = b.story.choose_choice_index(idx);
            }
            else
            {
                println!("No choice found for outcome tag '{choice_tag}', using first available");
                if !choices.is_empty()
                {
                    let _ = b.story.choose_choice_index(0);
                }
            }

            if b.story.can_continue()
            {
                let (text, parry, display_time) = advance_story(&mut b.story);
                Some((text, parry, display_time))
            }
            else
            {
                None
            }
        });

        match next
        {
            Some(Some((text, parry, display_time))) =>
            {
                world.dialog_bubbles.with_mut(bubble_entity, |b| {
                    b.current_text = text;
                    b.correct_parry = parry;
                    b.display_time = display_time;
                    b.phase = DialogPhase::Displaying {
                        timer: display_time,
                    };
                });
                println!(
                    "Dialog advanced: '{}'",
                    world
                        .dialog_bubbles
                        .with(bubble_entity, |b| b.current_text.clone())
                        .unwrap_or_default()
                );
            }

            Some(None) =>
            {
                world.despawn(bubble_entity);
                println!("Dialog story finished");
            }

            None =>
            {}
        }
    }
}

fn advance_story(story: &mut Story) -> (String, Option<ParryButton>, f32)
{
    let mut full_text = String::new();
    let mut parry: Option<ParryButton> = None;
    let mut display_time = DEFAULT_DISPLAY_TIME;

    while story.can_continue()
    {
        match story.cont()
        {
            Ok(line) => full_text.push_str(&line),
            Err(e) =>
            {
                eprintln!("Story error: {e}");
                break;
            }
        }

        if let Ok(tags) = story.get_current_tags()
        {
            for tag in tags
            {
                let tag = tag.trim().to_string();
                let tag = tag.as_str();
                if let Some(val) = tag.strip_prefix(PARRY_TAG_PREFIX)
                {
                    parry = ParryButton::from_tag(val.trim());
                }
                else if let Some(val) = tag.strip_prefix(TIMER_TAG_PREFIX)
                {
                    if let Ok(t) = val.trim().parse::<f32>()
                    {
                        display_time = t;
                    }
                }
            }
        }
    }

    (full_text.trim().to_string(), parry, display_time)
}
