import { defineStore, acceptHMRUpdate } from 'pinia';
import { socket } from 'boot/socketio';
import { useSocketStore } from 'stores/socket';

import type {
  CurrentLocation,
  LocationItemUpdate,
  LocationMarkUpdateResponse,
  LocationQueue,
  NominatimResponse,
  QueueData,
  SimulationControlResponse,
  SimulationStatus,
} from 'components/models';



export const useSimulationStore = defineStore('simulationStore', {
  state: () => {
    return {
      currentLocation: null as CurrentLocation | null | undefined,
      gpsNoise: null as boolean | undefined | null,
      locationQueueData: {} as LocationQueue,
      locationQueueDeletedItems: [] as string[],
      locationQueueOrder: [] as string[],
      simulationQueueLength: 0 as number | null | undefined,
      simulationRunning: false as boolean | undefined | null,
      simulationState: null as string | null | undefined,
      testMode: null as boolean | undefined | null,
    };
  },
  getters: {},
  actions: {
    bindEvents() {
      const socketStore = useSocketStore();
      const debugLog = socketStore.debugLog;
      // simulation_status
      socket.on('simulation_status', (data: SimulationStatus): void => {
        if (debugLog) {
          console.log('event: simulation_status received: ', data);
        }
        console.log('updating currentLocation', data);
        this.currentLocation = {
          loc_id: data.loc_id,
          latitude: data.latitude,
          longitude: data.longitude,
        };
      });

      // queue_data_update
      socket.on('queue_data_update', (inData: QueueData): void => {
        if (debugLog) {
          console.log('QueueUpdate received: ', inData);
        }
        this.digestQueueUpdate(inData);
      });
      // location_item_update
      socket.on('location_item_update', (inData: LocationItemUpdate): void => {
        console.log('Location item update received, data: ', inData);
        this.locationQueueData[inData.loc_id] = inData.data;
      });
    },
    simulationControl({
      command,
      latitude,
      longitude,
      loc_id,
      delay,
      address,
    }: {
      command: string;
      latitude?: number | null | undefined;
      longitude?: number | null | undefined;
      loc_id?: string | null | undefined;
      delay?: number | null | undefined;
      address?: NominatimResponse | string | null | undefined;
    }) {
      const socketStore = useSocketStore();
      const debugLog = socketStore.debugLog;
      let fnctRtn: { sts: string; msg?: string | undefined } = { sts: '', msg: '' };
      switch (command) {
        case 'start':
          if (debugLog) {
            console.log('socketStore: got command: start');
          }
          if (
            this.simulationRunning ||
            this.simulationState == 'RUNNING' ||
            this.simulationState == 'PAUSED'
          ) {
            fnctRtn = { sts: 'error', msg: 'Simulation is already running' };
            throw new Error('Simulation is already running' + this.simulationState);
          }
          if (debugLog) {
            console.log('Emitting simulation_control: start');
          }
          socket.emit(
            'simulation_control',
            { command: 'start' },
            (response: SimulationControlResponse) => {
              if (response.command_status == 'ERROR') {
                fnctRtn = { sts: 'error', msg: response.message?.toString() };
                throw new Error(response.message);
              } else {
                if (!response.data?.simulation_queue) {
                  fnctRtn = { sts: 'error', msg: 'Simulation queue data missing' };
                  throw new Error(response.message);
                } else {
                  fnctRtn = { sts: 'OK', msg: response.message?.toString() };
                  this.digestQueueUpdate(response.data.simulation_queue);
                }
                if (debugLog) {
                  console.log('response from simulate_control_start: ', response);
                }
                //              return response.message;
              }
            },
          );
          break;
        case 'test-mode':
          socket.emit(
            'simulation_control',
            { command: 'test-mode' },
            (response: SimulationControlResponse) => {
              if (response.command_status === 'ERROR') {
                throw new Error(response.message);
              } else {
                if (!response.data?.simulation_queue) {
                  fnctRtn = { sts: 'error', msg: 'Simulation queue data missing' };
                  throw new Error(response.message);
                } else {
                  fnctRtn = { sts: 'OK', msg: response.message?.toString() };
                  this.digestQueueUpdate(response.data.simulation_queue);
                }
                if (debugLog) {
                  console.log('response from simulate_control_test-mode: ', response);
                }
                return response.message;
              }
            },
          );
          break;
        case 'gps-noise':
          socket.emit(
            'simulation_control',
            { command: 'gps-noise' },
            (response: SimulationControlResponse) => {
              if (response.command_status === 'ERROR') {
                throw new Error(response.message);
              } else {
                if (!response.data?.simulation_queue) {
                  fnctRtn = { sts: 'error', msg: 'Simulation queue data missing' };
                  throw new Error(response.message);
                } else {
                  fnctRtn = { sts: 'OK', msg: response.message?.toString() };
                  this.digestQueueUpdate(response.data.simulation_queue);
                }
                if (debugLog) {
                  console.log('response from simulate_control_gps-noise: ', response);
                }
                return response.message;
              }
            },
          );
          break;
        case 'pause':
          if (this.simulationState !== 'RUNNING') {
            throw new Error('Simulation is not running');
          }
          socket.emit(
            'simulation_control',
            { command: 'pause' },
            (response: SimulationControlResponse) => {
              if (response.command_status === 'ERROR') {
                throw new Error(response.message);
              } else {
                if (!response.data?.simulation_queue) {
                  fnctRtn = { sts: 'error', msg: 'Simulation queue data missing' };
                  throw new Error(response.message);
                } else {
                  fnctRtn = { sts: 'OK', msg: response.message?.toString() };
                  this.digestQueueUpdate(response.data.simulation_queue);
                }
                if (debugLog) {
                  console.log('response from simulate_control_pause: ', response);
                }
                return response.message;
              }
            },
          );
          break;
        case 'resume':
          if (this.simulationState !== 'PAUSED') {
            throw new Error('Simulation is not paused');
          }
          socket.emit('simulation_control', { command: 'resume' }, (response) => {
            if (response.command_status == 'ERROR') {
              throw new Error(response.message);
            } else {
              if (!response.data?.simulation_queue) {
                fnctRtn = { sts: 'error', msg: 'Simulation queue data missing' };
                throw new Error(response.message);
              } else {
                fnctRtn = { sts: 'OK', msg: response.message?.toString() };
                this.digestQueueUpdate(response.data.simulation_queue);
              }
              if (debugLog) {
                console.log('response from simulate_control_resume: ', response);
              }
              return response.message;
            }
          });
          break;
        case 'clear':
          if (this.simulationQueueLength == 0) {
            throw new Error('Simulation queue is empty');
          }
          socket.emit('simulation_control', { command: 'clear' }, (response) => {
            if (response.command_status == 'ERROR') {
              throw new Error(response.message);
            } else {
              if (!response.data?.simulation_queue) {
                fnctRtn = { sts: 'error', msg: 'Simulation queue data missing' };
                throw new Error(response.message);
              } else {
                fnctRtn = { sts: 'OK', msg: response.message?.toString() };
                this.digestQueueUpdate(response.data.simulation_queue);
              }
              if (debugLog) {
                console.log('response from simulate_control_clear: ', response);
              }
              return response.message;
            }
          });
          break;
        case 'reset':
          if (this.simulationQueueLength == 0) {
            throw new Error('Simulation queue is empty');
          }
          socket.emit('simulation_control', { command: 'reset' }, (response) => {
            if (response.command_status == 'ERROR') {
              throw new Error(response.message);
            } else {
              if (!response.data?.simulation_queue) {
                fnctRtn = { sts: 'error', msg: 'Simulation queue data missing' };
                throw new Error(response.message);
              } else {
                fnctRtn = { sts: 'OK', msg: response.message?.toString() };
                this.digestQueueUpdate(response.data.simulation_queue);
              }
              if (debugLog) {
                console.log('response from simulate_control_reset: ', response);
              }
              return response.message;
            }
          });
          break;
        case 'end':
          if (this.simulationState == 'ENDED' || !this.simulationRunning) {
            throw new Error('Simulation has already ended');
          }
          socket.emit('simulation_control', { command: 'end' }, (response) => {
            if (response.command_status == 'ERROR') {
              throw new Error(response.message);
            } else {
              if (!response.data?.simulation_queue) {
                fnctRtn = { sts: 'error', msg: 'Simulation queue data missing' };
                throw new Error(response.message);
              } else {
                fnctRtn = { sts: 'OK', msg: response.message?.toString() };
                this.digestQueueUpdate(response.data.simulation_queue);
              }
              if (debugLog) {
                console.log('response from simulate_control_end: ', response);
              }
              return response.message;
            }
          });
          break;
        case 'add':
          if (!latitude || !longitude) {
            throw new Error('latitude or longitude not set');
          }
          if (!address) {
            address = '';
          }
          socket.emit(
            'simulation_control',
            {
              command: 'add',
              latitude: latitude,
              longitude: longitude,
              delay: delay,
              address: address,
            },
            (response) => {
              if (response.command_status == 'ERROR') {
                throw new Error(response.message);
              } else {
                if (!response.data?.simulation_queue) {
                  fnctRtn = { sts: 'error', msg: 'Simulation queue data missing' };
                  throw new Error(response.message);
                } else {
                  fnctRtn = { sts: 'OK', msg: response.message?.toString() };
                  this.digestQueueUpdate(response.data.simulation_queue);
                }
                if (debugLog) {
                  console.log('response from simulate_control_add: ', response);
                }
                return response.message;
              }
            },
          );
          break;
        case 'delete':
          if (!loc_id) {
            throw new Error('loc_id not set.');
          }
          socket.emit('simulation_control', { command: 'delete', loc_id: loc_id }, (response) => {
            if (response.command_status == 'ERROR') {
              throw new Error(response.message);
            } else {
              if (!response.data?.simulation_queue) {
                fnctRtn = { sts: 'error', msg: 'Simulation queue data missing' };
                throw new Error(response.message);
              } else {
                fnctRtn = { sts: 'OK', msg: response.message?.toString() };
                this.digestQueueUpdate(response.data.simulation_queue);
              }
              if (debugLog) {
                console.log('response from simulate_control_delete: ', response);
              }
              return response.message;
            }
          });
          this.updateLocationMark(loc_id, 'status', 'deleted');
          break;
        case 'next':
          socket.emit('simulation_control', { command: 'next' }, (response) => {
            if (response.command_status == 'ERROR') {
              throw new Error(response.message);
            } else {
              this.simulationState = response.data?.simulation_queue?.state;
              if (debugLog) {
                console.log('response from simulate_control_next: ', response);
              }
              return response.message;
            }
          });
          break;
        default:
          fnctRtn = { sts: 'error', msg: 'Invalid command' };
          throw new Error('Invalid command');
      }
      return fnctRtn;
    },
    updateLocationMark(loc_id: string, key: string, value: string | number) {
      const socketStore = useSocketStore();
      const debugLog = socketStore.debugLog;
      let fnctRtn: { command_status: string; message: string };
      if (debugLog) {
        console.log(
          'socketStore: Update LocationMark request, loc_id: %s, key: %s, new value: %s',
          loc_id,
          key,
          value,
        );
      }
      if (!this.locationQueueData[loc_id]) {
        fnctRtn = {
          command_status: 'error',
          message: 'Location Id: ' + loc_id + ' is not in the simulation queue',
        };
        throw new Error('loc_id ' + loc_id + 'not found');
      }
      if (debugLog) {
        console.log('Emitting LocationItem Update');
      }
      socket.emit(
        'location_item_update',
        { loc_id: loc_id, key: key, value: value },
        (response: LocationMarkUpdateResponse) => {
          if (response.command_status == 'ERROR') {
            fnctRtn = { command_status: 'error', message: response.message?.toString() };
            throw new Error(response.message);
          } else {
            fnctRtn = { command_status: 'OK', message: response.message?.toString() };
            if (debugLog) {
              console.log('response from backend: ', response);
            }
            this.locationQueueData[loc_id] = response.data;
          }
          return fnctRtn;
        },
      );
    },
    updateLocationQueueOrder(newOrder: string[]) {
      const socketStore = useSocketStore();
      const debugLog = socketStore.debugLog;
      let fnctRtn: { command_status: string; message: string };
      if (debugLog) {
        console.log('socketStore: Update Location Queue Order, new order: %s', newOrder);
      }
      socket.emit('queue_order_update', { newOrder: newOrder }, (response) => {
        if (response.command_status == 'ERROR') {
          fnctRtn = { command_status: 'error', message: response.message?.toString() };
          throw new Error(response.message);
        } else {
          fnctRtn = { command_status: 'OK', message: response.message?.toString() };
          if (debugLog) {
            console.log('response from queue_order_update: ', response);
          }
          this.locationQueueOrder = response.data;
        }
        return fnctRtn;
      });
    },
    digestQueueUpdate(data: QueueData): void {
      console.log('digesting QueueUpdate: ', data);
      this.simulationRunning = data.active;
      console.log('Setting SimulationState to %s', data.state);
      this.simulationState = data.state;
      this.simulationQueueLength = data.order.length;
      this.locationQueueData = data.data;
      this.locationQueueOrder = data.order;
      this.locationQueueDeletedItems = data.deleted_items;
      this.testMode = data.test_mode;
      this.gpsNoise = data.gps_noise;
    },
    digestCurrentLocation(data: CurrentLocation): void {
      this.currentLocation = {
        loc_id: data.loc_id,
        latitude: data.latitude,
        longitude: data.longitude,
        next_move: data.next_move,
      };
    }
  },
});

if (import.meta.hot) {
  import.meta.hot.accept(acceptHMRUpdate(useSimulationStore, import.meta.hot));
}
