using Content.Server.Projectiles.Components;
using Content.Server.Weapon.Ranged.Barrels.Components;
using Content.Shared.PowerCell.Components;
using Content.Shared.Weapons.Ranged.Barrels.Components;
using Robust.Shared.Containers;
using Robust.Shared.Map;

namespace Content.Server.Weapon.Ranged;

public sealed partial class GunSystem
{
    private void OnBatteryInit(EntityUid uid, BatteryBarrelComponent component, ComponentInit args)
    {
        if (component.AmmoPrototype != null)
        {
            component.AmmoContainer = uid.EnsureContainer<ContainerSlot>($"{component.GetType()}-ammo-container");
        }

        component.Dirty(EntityManager);
    }

    private void OnBatteryMapInit(EntityUid uid, BatteryBarrelComponent component, MapInitEvent args)
    {
        UpdateBatteryAppearance(component);
    }

    private void OnCellSlotUpdated(EntityUid uid, BatteryBarrelComponent component, PowerCellChangedEvent args)
    {
        UpdateBatteryAppearance(component);
    }

    public void UpdateBatteryAppearance(BatteryBarrelComponent component)
    {
        if (!EntityManager.TryGetComponent(component.Owner, out AppearanceComponent? appearanceComponent)) return;

        appearanceComponent.SetData(MagazineBarrelVisuals.MagLoaded, _cell.TryGetBatteryFromSlot(component.Owner, out _));
        appearanceComponent.SetData(AmmoVisuals.AmmoCount, component.ShotsLeft);
        appearanceComponent.SetData(AmmoVisuals.AmmoMax, component.Capacity);
    }

    public EntityUid? PeekAmmo(BatteryBarrelComponent component)
    {
        // Spawn a dummy entity because it's easier to work with I guess
        // This will get re-used for the projectile
        var ammo = component.AmmoContainer.ContainedEntity;
        if (ammo == null)
        {
            ammo = EntityManager.SpawnEntity(component.AmmoPrototype, Transform(component.Owner).Coordinates);
            component.AmmoContainer.Insert(ammo.Value);
        }

        return ammo.Value;
    }

    public EntityUid? TakeProjectile(BatteryBarrelComponent component, EntityCoordinates spawnAt)
    {
        if (!_cell.TryGetBatteryFromSlot(component.Owner, out var capacitor))
            return null;

        if (capacitor.CurrentCharge < component.LowerChargeLimit)
            return null;

        // Can fire confirmed
        // Multiply the entity's damage / whatever by the percentage of charge the shot has.
        EntityUid? entity;
        var chargeChange = Math.Min(capacitor.CurrentCharge, component.BaseFireCost);
        if (capacitor.UseCharge(chargeChange) < component.LowerChargeLimit)
        {
            // Handling of funny exploding cells.
            return null;
        }
        var energyRatio = chargeChange / component.BaseFireCost;

        if (component.AmmoContainer.ContainedEntity != null)
        {
            entity = component.AmmoContainer.ContainedEntity;
            component.AmmoContainer.Remove(entity.Value);
            Transform(entity.Value).Coordinates = spawnAt;
        }
        else
        {
            entity = EntityManager.SpawnEntity(component.AmmoPrototype, spawnAt);
        }

        if (TryComp(entity.Value, out ProjectileComponent? projectileComponent))
        {
            if (energyRatio < 1.0)
            {
                projectileComponent.Damage *= energyRatio;
            }
        }
        else if (TryComp(entity.Value, out HitscanComponent? hitscanComponent))
        {
            hitscanComponent.Damage *= energyRatio;
            hitscanComponent.ColorModifier = energyRatio;
        }
        else
        {
            throw new InvalidOperationException("Ammo doesn't have hitscan or projectile?");
        }

        // capacitor.UseCharge() triggers a PowerCellChangedEvent which will cause appearance to be updated.
        // So let's not double-call UpdateAppearance() here.
        return entity.Value;
    }
}
