// Copyright (C) Call of Nil contributors
// SPDX-License-Identifier: AGPL-3.0-only

use proc_macro::TokenStream;
use quote::quote;
use syn::DeriveInput;

#[expect(clippy::too_many_lines)]
pub fn impl_building(ast: &DeriveInput) -> TokenStream {
  let name = &ast.ident;
  let stream = quote! {
    mod __impl_building {
      use super::#name;
      use crate::error::Result;
      use crate::infrastructure::prelude::*;
      use crate::ranking::score::Score;
      use crate::resources::prelude::*;

      #[bon::bon]
      impl #name {
        pub fn with_level(level: BuildingLevel) -> Self {
          let mut building = Self::default();
          building.set_level(level);
          building
        }

        #[inline]
        pub fn with_min_level() -> Self {
          Self::with_level(Self::MIN_LEVEL)
        }

        #[inline]
        pub fn with_max_level() -> Self {
          Self::with_level(Self::MAX_LEVEL)
        }

        #[inline]
        pub fn with_random_level() -> Self {
          Self::with_random_level_in()
            .min(Self::MIN_LEVEL)
            .max(Self::MAX_LEVEL)
            .call()
        }

        #[builder]
        pub fn with_random_level_in(
          #[builder(default = #name::MIN_LEVEL)] min: BuildingLevel,
          #[builder(default = #name::MAX_LEVEL)] max: BuildingLevel,
        ) -> #name {
          let min = u8::from(min.max(#name::MIN_LEVEL));
          let max = u8::from(max.min(#name::MAX_LEVEL));
          let level = rand::random_range(min..=max);
          #name::with_level(BuildingLevel::new(level))
        }
      }

      #[automatically_derived]
      impl Building for #name {
        fn id(&self) -> BuildingId {
          Self::ID
        }

        fn is_enabled(&self) -> bool {
          self.enabled
        }

        fn toggle(&mut self, enabled: bool) {
          self.enabled = enabled;
        }

        fn level(&self) -> BuildingLevel {
          self.level
        }

        fn min_level(&self) -> BuildingLevel {
          Self::MIN_LEVEL
        }

        fn max_level(&self) -> BuildingLevel {
          Self::MAX_LEVEL
        }

        fn set_level(&mut self, level: BuildingLevel) {
          self.level = level.clamp(Self::MIN_LEVEL, Self::MAX_LEVEL);
        }

        fn increase_level_by(&mut self, amount: u8) {
          self.set_level(self.level + amount)
        }

        fn decrease_level_by(&mut self, amount: u8) {
          self.set_level(self.level - amount)
        }

        fn min_cost(&self) -> Cost {
          Self::MIN_COST
        }

        fn max_cost(&self) -> Cost {
          Self::MAX_COST
        }

        fn food_ratio(&self) -> ResourceRatio {
          Self::FOOD_RATIO
        }

        fn iron_ratio(&self) -> ResourceRatio {
          Self::IRON_RATIO
        }

        fn stone_ratio(&self) -> ResourceRatio {
          Self::STONE_RATIO
        }

        fn wood_ratio(&self) -> ResourceRatio {
          Self::WOOD_RATIO
        }

        fn maintenance(&self, stats: &BuildingStatsTable) -> Result<Maintenance> {
          Ok(stats.get(self.level)?.maintenance)
        }

        fn maintenance_ratio(&self) -> MaintenanceRatio {
          Self::MAINTENANCE_RATIO
        }

        fn min_workforce(&self) -> Workforce {
          Self::MIN_WORKFORCE
        }

        fn max_workforce(&self) -> Workforce {
          Self::MAX_WORKFORCE
        }

        fn score(&self, stats: &BuildingStatsTable) -> Result<Score> {
          if self.level == BuildingLevel::ZERO {
            Ok(Score::ZERO)
          } else {
            Ok(stats.get(self.level)?.score)
          }
        }

        fn min_score(&self) -> Score {
          Self::MIN_SCORE
        }

        fn max_score(&self) -> Score {
          Self::MAX_SCORE
        }

        fn infrastructure_requirements(&self) -> &InfrastructureRequirements {
          &Self::INFRASTRUCTURE_REQUIREMENTS
        }
      }
    }
  };

  stream.into()
}
