import BaseService from './BaseService.js';
import TaxiMap from '../models/taxi/taxiMap.js';
import TaxiMapType from '../models/taxi/taxiMapType.js';
import TaxiMapTile from '../models/taxi/taxiMapTile.js';
import TaxiStreetName from '../models/taxi/taxiStreetName.js';
import TaxiMapTileStreet from '../models/taxi/taxiMapTileStreet.js';
import TaxiMapTileHouse from '../models/taxi/taxiMapTileHouse.js';

class TaxiMapService extends BaseService {
  constructor() {
    super();
  }

  /**
   * Holt alle verfügbaren Map-Typen
   */
  async getMapTypes() {
    try {
      const mapTypes = await TaxiMapType.findAll({
        where: { isActive: true },
        order: [['name', 'ASC']]
      });
      return mapTypes;
    } catch (error) {
      console.error('Error getting map types:', error);
      throw error;
    }
  }

  /**
   * Holt alle verfügbaren Maps
   */
  async getMaps() {
    try {
      const maps = await TaxiMap.findAll({
        where: { isActive: true },
        include: [
          { model: TaxiMapType, as: 'mapType' },
          { model: TaxiMapTile, as: 'tiles' },
          { 
            model: TaxiMapTileStreet, 
            as: 'tileStreets',
            include: [
              { model: TaxiStreetName, as: 'streetNameH' },
              { model: TaxiStreetName, as: 'streetNameV' }
            ]
          },
          { model: TaxiMapTileHouse, as: 'tileHouses' }
        ],
        order: [['name', 'ASC']]
      });
      return maps;
    } catch (error) {
      console.error('Error getting maps:', error);
      throw error;
    }
  }

  /**
   * Holt eine spezifische Map
   */
  async getMapById(mapId) {
    try {
      const map = await TaxiMap.findOne({
        where: { 
          id: mapId,
          isActive: true 
        },
        include: [
          { model: TaxiMapType, as: 'mapType' },
          { model: TaxiMapTile, as: 'tiles' },
          { 
            model: TaxiMapTileStreet, 
            as: 'tileStreets',
            include: [
              { model: TaxiStreetName, as: 'streetNameH' },
              { model: TaxiStreetName, as: 'streetNameV' }
            ]
          },
          { model: TaxiMapTileHouse, as: 'tileHouses' }
        ]
      });
      return map;
    } catch (error) {
      console.error('Error getting map by ID:', error);
      throw error;
    }
  }


  /**
   * Holt die Standard-Map
   */
  async getDefaultMap() {
    try {
      const map = await TaxiMap.findOne({
        where: { 
          isDefault: true,
          isActive: true 
        },
        include: [{
          model: TaxiMapType,
          as: 'mapType'
        }]
      });
      return map;
    } catch (error) {
      console.error('Error getting default map:', error);
      throw error;
    }
  }

  /**
   * Erstellt eine neue Map
   */
  async createMap(mapData) {
    try {
      const { tiles, tileStreetNames, tileHouses, ...mapFields } = mapData;
      // mapData JSON ist entfernt – map erstellen ohne mapData
      const map = await TaxiMap.create(mapFields);
      // Tiles upsert (optional)
      if (Array.isArray(tiles) && tiles.length > 0) {
        await this.upsertTiles(map.id, tiles);
      }
      // Street names (optional)
      if (tileStreetNames && Object.keys(tileStreetNames).length > 0) {
        await this.upsertTileStreetNames(map.id, tileStreetNames);
      }
      if (tileHouses && Object.keys(tileHouses).length > 0) {
        await this.upsertTileHouses(map.id, tileHouses);
      }
      return await this.getMapById(map.id);
    } catch (error) {
      console.error('Error creating map:', error);
      throw error;
    }
  }

  /**
   * Aktualisiert eine Map
   */
  async updateMap(mapId, updateData) {
    try {
      const { tiles, tileStreetNames, tileHouses, ...mapFields } = updateData;
      const [updatedRowsCount] = await TaxiMap.update(mapFields, {
        where: { id: mapId }
      });
      
      if (updatedRowsCount === 0) {
        throw new Error('Map not found');
      }
      
      // Tiles upsert (optional)
      if (Array.isArray(tiles)) {
        await this.upsertTiles(mapId, tiles);
      }
      if (tileStreetNames && Object.keys(tileStreetNames).length > 0) {
        await this.upsertTileStreetNames(mapId, tileStreetNames);
      }
      if (tileHouses && Object.keys(tileHouses).length > 0) {
        await this.upsertTileHouses(mapId, tileHouses);
      }
      return await this.getMapById(mapId);
    } catch (error) {
      console.error('Error updating map:', error);
      throw error;
    }
  }

  /**
   * Speichert Straßennamen-Belegungen für eine Map
   * @param {number} mapId
   * @param {{[cellKey:string]:{streetNameH?:string, streetNameV?:string}}} tileNames
   */
  async upsertTileStreetNames(mapId, tileNames) {
    const entries = Object.entries(tileNames || {});
    for (const [cellKey, data] of entries) {
      const [x, y] = cellKey.split(',').map(Number);
      const record = { mapId, x, y };
      // Street names: findOrCreate
      if (data.streetNameH) {
        const [snH] = await TaxiStreetName.findOrCreate({ where: { name: data.streetNameH }, defaults: { name: data.streetNameH } });
        record.streetNameHId = snH.id;
      } else {
        record.streetNameHId = null;
      }
      if (data.streetNameV) {
        const [snV] = await TaxiStreetName.findOrCreate({ where: { name: data.streetNameV }, defaults: { name: data.streetNameV } });
        record.streetNameVId = snV.id;
      } else {
        record.streetNameVId = null;
      }

      const [row] = await TaxiMapTileStreet.findOrCreate({ where: { mapId, x, y }, defaults: record });
      await row.update(record);
    }
    return { success: true };
  }

  /**
   * Upsert Tiles (x,y,tileType,meta?) pro Map
   * Erwartet: tiles: Array<{x:number,y:number,tileType:string, meta?:object}>
   */
  async upsertTiles(mapId, tiles) {
    for (const tile of tiles) {
      const { x, y, tileType, meta } = tile;
      if (typeof x !== 'number' || typeof y !== 'number' || !tileType) continue;
      const [row] = await TaxiMapTile.findOrCreate({
        where: { mapId, x, y },
        defaults: { mapId, x, y, tileType, meta: meta || null }
      });
      // trafficLight kann in meta.trafficLight oder künftig in eigener Spalte liegen
      const trafficLight = !!(meta && meta.trafficLight);
      await row.update({ tileType, meta: meta && Object.keys(meta).length ? meta : null, trafficLight });
    }
    return { success: true };
  }

  /**
   * Speichert Häuser pro Tile (je Ecke eine Zeile)
   * @param {number} mapId
   * @param {{[cellKey:string]: {[corner:string]: number}}} tileHouses
   */
  async upsertTileHouses(mapId, tileHouses) {
    // Löschen und neu anlegen pro Zelle ist am einfachsten und robust
    const entries = Object.entries(tileHouses || {});
    for (const [cellKey, cornerMap] of entries) {
      const [x, y] = cellKey.split(',').map(Number);
      await TaxiMapTileHouse.destroy({ where: { map_id: mapId, x, y } });
      for (const [corner, rotation] of Object.entries(cornerMap || {})) {
        await TaxiMapTileHouse.create({ mapId, x, y, corner, rotation: Number(rotation) || 0 });
      }
    }
    return { success: true };
  }

  /**
   * Löscht eine Map (soft delete)
   */
  async deleteMap(mapId) {
    try {
      const [updatedRowsCount] = await TaxiMap.update(
        { isActive: false },
        { where: { id: mapId } }
      );
      
      if (updatedRowsCount === 0) {
        throw new Error('Map not found');
      }
      
      return { success: true };
    } catch (error) {
      console.error('Error deleting map:', error);
      throw error;
    }
  }

  /**
   * Setzt eine Map als Standard
   */
  async setDefaultMap(mapId) {
    try {
      // Entferne Standard-Status von allen anderen Maps
      await TaxiMap.update(
        { isDefault: false },
        { where: { isDefault: true } }
      );
      
      // Setze neue Standard-Map
      const [updatedRowsCount] = await TaxiMap.update(
        { isDefault: true },
        { where: { id: mapId } }
      );
      
      if (updatedRowsCount === 0) {
        throw new Error('Map not found');
      }
      
      return { success: true };
    } catch (error) {
      console.error('Error setting default map:', error);
      throw error;
    }
  }

  /**
   * Initialisiert Standard-Map-Typen
   */
  async initializeMapTypes() {
    try {
      const mapTypes = [
        { name: 'Corner Bottom Left', tileType: 'cornerBottomLeft', description: 'Bottom left corner tile' },
        { name: 'Corner Bottom Right', tileType: 'cornerBottomRight', description: 'Bottom right corner tile' },
        { name: 'Corner Top Left', tileType: 'cornerTopLeft', description: 'Top left corner tile' },
        { name: 'Corner Top Right', tileType: 'cornerTopRight', description: 'Top right corner tile' },
        { name: 'Horizontal', tileType: 'horizontal', description: 'Horizontal road tile' },
        { name: 'Vertical', tileType: 'vertical', description: 'Vertical road tile' },
        { name: 'Cross', tileType: 'cross', description: 'Cross intersection tile' },
        { name: 'Fuel Horizontal', tileType: 'fuelHorizontal', description: 'Horizontal road with fuel station' },
        { name: 'Fuel Vertical', tileType: 'fuelVertical', description: 'Vertical road with fuel station' },
        { name: 'T-Left', tileType: 'tLeft', description: 'T-junction facing left' },
        { name: 'T-Right', tileType: 'tRight', description: 'T-junction facing right' },
        { name: 'T-Up', tileType: 'tUp', description: 'T-junction facing up' },
        { name: 'T-Down', tileType: 'tDown', description: 'T-junction facing down' }
      ];

      for (const mapType of mapTypes) {
        await TaxiMapType.findOrCreate({
          where: { name: mapType.name },
          defaults: mapType
        });
      }

      console.log('Taxi map types initialized');
    } catch (error) {
      console.error('Error initializing map types:', error);
      throw error;
    }
  }

  /**
   * Erstellt eine Standard-Map
   */
  async createDefaultMap() {
  }
}

export default TaxiMapService;
