import { interval } from "rxjs";
import { GoodType } from "./goods/good-type.enum";
import { GOODS_DATA } from "./goods/good-config";
import { Good } from "./goods/good.interface";
import { Ship } from "./ships/ship.model";
import { GameService } from "../service/game.service";
import { Factory } from "./factories/factory.model";

export class Planet {
  public population: number = 100;
  public name: string = "";
  public image: string;

  public updateModifier = 1; // für Simulation
  private goods: Map<string, Good> = new Map();
  private updateSubscription = interval(1000).subscribe(() => {this.update(this.updateModifier)}); // alle 5s

  public isGrowing: boolean = false;
  private populationGrowthRate = 0.002; // Basiswachstum pro Tick (%)
  private populationDeclineRate = 0.005; // Basisrückgang bei Mangel (%)
  public demandSecondsBuffer = 160; // Anfrage immer mindestens 30 Sekunden überleben

  private productionLevel: Map<GoodType, number> = new Map();
  private dockedShips: Ship[] = [];
  public dockCapacity: number = 1;
  private shipsWaitingForDocking: Ship[] = [];

  private factory: Factory | undefined;

  private gameService: GameService;

  hasHarbour = false;


  constructor(config: PlanetInit, gameService: GameService) {
    this.name = config.name;
    this.image = config.name.toLowerCase().split(' ').join('-')
    this.gameService = gameService;
    /** set all empty */
    Object.values(GoodType).forEach(c => {
      this.goods.set(c, {
        amount: 0,
        demandRate: 0,
        productionRate: 0,
        productionStorage: 0,
        type: c
      })
    })


    config.initialGoods.forEach(good => {
      const base = GOODS_DATA[good.type];
      this.goods.set(good.type, {
        type: good.type,
        amount: good.amount,
        demandRate: base.baseDemand,
        productionRate: base.baseProduction  * (good.productionBonus ?? 0),
        productionStorage: base.storageLimit
      })
    });
    console.log(this.getAllGoods())
    this.updatePopulation(0)

    setTimeout(() => {
      this.getStatus();
    }, 10000)
  }

  adjustPopulationDemands(good: Good) {
    switch(good.type) {
      case GoodType.Erz:
        if (this.population > 250) {
          good.demandRate = GOODS_DATA.Erz.baseDemand
        } else {
          good.demandRate = 0;
        }
        break;
      case GoodType.Bauteile:
        if (this.population > 700) {
          good.demandRate = GOODS_DATA.Bauteile.baseDemand
        } else {
          good.demandRate = 0;
        }
        break;
      case GoodType.Elektronik:
        if (this.population > 900) {
          good.demandRate = GOODS_DATA.Elektronik.baseDemand
        } else {
          good.demandRate = 0;
        }
        break;
      case GoodType.Metall:
        if (this.population > 350) {
          good.demandRate = GOODS_DATA.Metall.baseDemand
        } else {
          good.demandRate = 0;
        }
        break;
      case GoodType.Treibstoff:
        if (this.population > 500) {
          good.demandRate = GOODS_DATA.Treibstoff.baseDemand
        } else {
          good.demandRate = 0;
        }
        break;
    }
  }
  

  private update(seconds: number): void {

    this.goods.forEach((good: Good, key: string) => {

      this.adjustPopulationDemands(good)
      const base = GOODS_DATA[good.type];
      const lvlMultiplier = this.productionLevel.get(good.type) ?? 1;
      

      if (base.isRawResource) {
        
        good.amount += good.productionRate * seconds * lvlMultiplier;

        // Min 0
        good.amount = Math.max(0, good.amount);
        if (good.productionRate && good.productionStorage) {
          good.amount = Math.min(good.amount, good.productionStorage)
        }
      } else {
        if (good.productionRate == 0) { return; }
        const depends = base.dependsOn;
        if (!depends) { return; }
        const depending = this.getAllGoods().filter(g => depends.includes(g.type));
        const amount = good.productionRate * seconds * lvlMultiplier;
        if (depending.some(d => d.amount < seconds * lvlMultiplier)) { return; }

        depending.map(d => {
          d.amount -= seconds * lvlMultiplier;
        });
        good.amount += amount;
      }
      
    });

    this.updatePopulation(seconds)


    return;

    // Debug-Ausgabe (später ersetzen durch Events/Callback/Service)



    
  }

  getGood(type: GoodType): Good | undefined {
    return this.goods.get(type);
  }

  getAllGoods(): Good[] {
    return Array.from(this.goods.values());
  }

  addGood(good: Good): void {
    this.goods.set(good.type, { ...good });
  }


  /**
   * Gets a list of goods that the planet needs to import
   * @returns Array of goods with their type and requested amount
   * For each good that:
   * - has no production
   * - has demand
   * - current amount is less than buffer demand
   * Returns 3x the buffer demand amount
   */
  get requestedGoods(): TradeInstance[] {
    const result: TradeInstance[] = [];

    const goods = this.getAllGoods().filter(g => g.demandRate);
    for (let good of goods) {
      const demandPerSecond = good.demandRate * this.population;
      const demand = this.demandSecondsBuffer * demandPerSecond;
      if (demand < good.amount) {
        continue; // Skip if we have enough in stock
      }

      const inTransit = this.goodsInTransit.filter(g => g.type == good.type).reduce((acc, succ) => acc + succ.amount, 0);

      
      if (demand < good.amount + inTransit) {
        continue; // Es werden genug geliefert.
      }

      result.push({
        amount: demand * 5 - good.amount - inTransit, // Request 3x buffer amount
        type: good.type,
        target: this.name,
      })
    }

    return result;
  }

  get offeredGoods(): TradeInstance[] {
    return this.getAllGoods().filter(g => g.productionRate && g.amount).map(g => { return { type: g.type, amount: g.amount - (g.demandRate * this.population * (this.demandSecondsBuffer * 1.2)), target: this.name }}).filter(g => g.amount > 0)
  }

  request(demandedGood: {type: GoodType, amount: number}) {
    const offers = this.offeredGoods;
    if (offers.length == 0) { return 0; }

    const good = offers.find(o => o.type == demandedGood.type);
    if (!good) { return 0; }

    const amount = Math.min(demandedGood.amount, good.amount);

    const store = this.getGood(demandedGood.type);
    if (!store) { return 0; }

    store.amount -= amount;
    return amount;
  }


  private updatePopulation(seconds: number): void {
    const demands = this.calculateNaturalDemand(seconds);
    let allSupplied = true;
    let notSupplied = 0;
  
    demands.forEach((amountNeeded, goodType) => {
      const available = this.goods.get(goodType)?.amount ?? 0;
      if (available < amountNeeded) {
        allSupplied = false;
        notSupplied++;
      }
    });
  
    if (allSupplied) {
      this.population += this.population * this.populationGrowthRate * seconds;
      this.isGrowing = true;
    } else {
      this.isGrowing = false;
      if (notSupplied > 1) {
        this.population -= this.population * this.populationDeclineRate * seconds;
        this.population = Math.max(this.population, 100);
      }
      
    }

    // Verbrauch abziehen
    demands.forEach((amountNeeded, goodType) => {
      const good = this.goods.get(goodType);
      if (good && good.amount >= amountNeeded) {
        good.amount -= amountNeeded;
      }
    });
  }


  /**
   * Calculates the natural resource demand based on population.
   * @param seconds Time interval in seconds
   * @returns Map of good types to their demand quantities
   * @private
   */
  private calculateNaturalDemand(seconds: number): Map<GoodType, number> {
    const demand = new Map<GoodType, number>();

    for (let good of this.getAllGoods()) {
      demand.set(good.type ,this.population * good.demandRate * seconds )
    }
  
    // for (const goodType in GOODS_DATA) {
    //   const config = GOODS_DATA[goodType as GoodType];
    //   if (config.baseDemand > 0) {
    //     demand.set(goodType as GoodType, this.population * config.baseDemand * seconds);
    //   }
    // }
  
    return demand;
  }

  getCriticalGoods(): GoodType[] {
    const critical: GoodType[] = [];
    this.getAllGoods().forEach(good => {
      if (good && good.amount < 10 && good.demandRate) { // unter 10 Einheiten = kritisch
        critical.push(good.type);
      }
    })
  
  
    return critical;
  }

  shipCanDock(ship: Ship): boolean {
    if (this.dockedShips.includes(ship)) { return true; } // bereits gedockt
    /** Dock ist voll */
    if (this.dockedShips.length >= this.dockCapacity) { 
      if (!this.shipsWaitingForDocking.includes(ship)) {
        /** Schiff auf Warteliste */
        this.shipsWaitingForDocking.push(ship);
      }
      return false;
    } 
    if (this.shipsWaitingForDocking.length == 0) {
      this.dock(ship)
      return true;
    } // keine wartenden schiffe
    /* Dock ist nicht voll, aber es gibt Wartende Schiffe */

    /* in warteschlange pushen */
    if (!this.shipsWaitingForDocking.includes(ship)) {
      this.shipsWaitingForDocking.push(ship)
    } else {
      /* Schiff ist schon registriert */
      const first = this.shipsWaitingForDocking.indexOf(ship) == 0; // ist an erster stelle?
      if (first) {
        this.dock(ship);
        return true;
      }
    }
    return false;
  }


  dock(ship: Ship) {
    if (!this.dockedShips.includes(ship)) {
      this.dockedShips.push(ship);
      this.shipsWaitingForDocking = this.shipsWaitingForDocking.filter(s => s != ship); // aus warteschlange entfernen
    }
  }

  undock(ship: Ship)  {
    this.dockedShips = this.dockedShips.filter(d => d != ship);
  }

  get goodsInTransit(): TradeInstance[] {
    return this.gameService.ships
      .flatMap(ship => ship.cargoSpace)
      .filter(item => item.target === this.name);
  }


  upgradeHarbour() {
    this.dockCapacity++;
  }


  buildHarbour() {
    this.hasHarbour = true;
    this.gameService.money -= 30000;
  }

  upgradeProduction() {
    if (this.gameService.money < this.productionLvlUpgradeCost) { return; }
    this.gameService.money -= this.productionLvlUpgradeCost;
    this.getAllGoods().filter(g => g.productionRate).forEach(good => {
      const lvl = this.productionLevel.get(good.type);
      if (lvl) {
        this.productionLevel.set(good.type, lvl + 1);
      } else {
        this.productionLevel.set(good.type, 2)
      }
      if (good.productionStorage) {
        good.productionStorage = good.productionStorage * 1.5;
      }

    })

    
  }

  getProductionLvl(good: Good): number {
    return this.productionLevel.get(good.type) ?? 1;
  }

  get productionLvlUpgradeCost(): number {
    const lvls = Array.from(this.productionLevel.values());
    lvls.push(1); // falls es leer ist
    const lvl = Math.max(...lvls);
    return lvl * 1000;
  }

  buildFactory() {
    this.factory = new Factory({
      planet: this,
      type: GoodType.Elektronik
    });
  }


  public getStatus(): void {
    console.log(`[${this.name}]: ${this.isGrowing ? '🚀' : '⬇️'}`);
    console.log('Population:', Math.round(this.population));
    console.log('Goods:\n', Array.from(this.goods.entries()).map(([type, good]) => 
      `${type}: ${Math.round(good.amount)} (Prod: ${good.productionRate}, Demand: ${good.demandRate})`
    ).join('\n'));
    // console.log('Docked Ships:', this.dockedShips.length);
    // console.log('Waiting Ships:', this.shipsWaitingForDocking.length);
    console.log('Production Levels:', Array.from(this.productionLevel.entries()).map(([type, level]) => 
      `${type}: ${level}`
    ).join(', '));
    console.log(`\n\n`)
}
}

export interface PlanetInit {
  name: string;
  initialGoods: {
    type: GoodType;
    amount: number;
    productionBonus?: number; // optional: z. B. 1.5 = 150% der Basis
  }[];
}

export interface TradeInstance {
  type: GoodType;
  amount: number;
  target: string;
}