import * as React from 'react';
import './App.scss';
import { MainMenu } from './Components/MainMenu/MainMenu';
import { ContainerModel, IContainerModel } from './Interfaces/ContainerModel';
import Editor, { IEditorState } from './Editor';
import { Configuration } from './Interfaces/Configuration';
import { Revive } from './utils/saveload';

export interface IHistoryState {
  MainContainer: IContainerModel | null
  SelectedContainer: IContainerModel | null
  SelectedContainerId: string
  TypeCounters: Record<string, number>
}

// App will never have props
// eslint-disable-next-line @typescript-eslint/no-empty-interface
interface IAppProps {
}

interface IAppState {
  configuration: Configuration
  history: IHistoryState[]
  historyCurrentStep: number
  isLoaded: boolean
}

export class App extends React.Component<IAppProps> {
  public state: IAppState;

  constructor(props: IAppProps) {
    super(props);
    this.state = {
      configuration: {
        AvailableContainers: [],
        AvailableSymbols: [],
        MainContainer: {
          Type: 'EmptyContainer',
          Width: 3000,
          Height: 200,
          Style: {}
        }
      },
      history: [],
      historyCurrentStep: 0,
      isLoaded: false
    };
  }

  componentDidMount(): void {
    const queryString = window.location.search;
    const urlParams = new URLSearchParams(queryString);
    const state = urlParams.get('state');

    if (state === null) {
      return;
    }

    fetch(state)
      .then(
        async(response) => await response.json(),
        (error) => { throw new Error(error); }
      )
      .then((data: IEditorState) => {
        this.LoadState(data);
      }, (error) => { throw new Error(error); });
  }

  public NewEditor(): void {
    // Fetch the configuration from the API
    fetchConfiguration()
      .then((configuration: Configuration) => {
      // Set the main container from the given properties of the API
        const MainContainer = new ContainerModel(
          null,
          {
            id: 'main',
            parentId: 'null',
            x: 0,
            y: 0,
            width: configuration.MainContainer.Width,
            height: configuration.MainContainer.Height,
            fillOpacity: 0,
            stroke: 'black'
          }
        );

        // Save the configuration and the new MainContainer
        // and default the selected container to it
        this.setState({
          configuration,
          history:
          [
            {
              MainContainer,
              SelectedContainer: MainContainer,
              TypeCounters: {}
            }
          ],
          historyCurrentStep: 0,
          isLoaded: true
        });
      }, (error) => {
        // TODO: Implement an alert component
        console.warn('[NewEditor] Could not fetch resource from API. Returning default.', error);
        const MainContainer = new ContainerModel(
          null,
          {
            id: 'main',
            parentId: 'null',
            x: 0,
            y: 0,
            width: DEFAULT_CONFIG.MainContainer.Width,
            height: DEFAULT_CONFIG.MainContainer.Height,
            fillOpacity: DEFAULT_CONFIG.MainContainer.Style.fillOpacity,
            stroke: DEFAULT_CONFIG.MainContainer.Style.stroke,
          }
        );

        // Save the configuration and the new MainContainer
        // and default the selected container to it
        this.setState({
          configuration: DEFAULT_CONFIG,
          history:
          [
            {
              MainContainer,
              SelectedContainer: MainContainer,
              TypeCounters: {}
            }
          ],
          historyCurrentStep: 0,
          isLoaded: true
        });
      });
  }

  public LoadEditor(files: FileList | null): void {
    if (files === null) {
      return;
    }
    const file = files[0];
    const reader = new FileReader();
    reader.addEventListener('load', () => {
      const result = reader.result as string;
      const editorState: IEditorState = JSON.parse(result);

      this.LoadState(editorState);
    });
    reader.readAsText(file);
  }

  private LoadState(editorState: IEditorState): void {
    Revive(editorState);

    this.setState({
      configuration: editorState.configuration,
      history: editorState.history,
      historyCurrentStep: editorState.historyCurrentStep,
      isLoaded: true
    });
  }

  public render(): JSX.Element {
    if (this.state.isLoaded) {
      return (
        <div>
          <Editor
            configuration={this.state.configuration}
            history={this.state.history}
            historyCurrentStep={this.state.historyCurrentStep}
          />
        </div>
      );
    } else {
      return (
        <div className='bg-blue-100 h-full w-full'>
          <MainMenu
            newEditor={() => this.NewEditor()}
            loadEditor={(files: FileList | null) => this.LoadEditor(files)}
          />
        </div>
      );
    }
  }
}

/**
 * Fetch the configuration from the API
 * @returns {Configation} The model of the configuration for the application
 */
export async function fetchConfiguration(): Promise<Configuration> {
  const url = `${import.meta.env.VITE_API_URL}`;
  // The test library cannot use the Fetch API
  // @ts-expect-error
  // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
  if (window.fetch) {
    return await fetch(url, {
      method: 'POST'
    })
      .then(async(response) =>
        await response.json()
      ) as Configuration;
  }
  return await new Promise((resolve) => {
    const xhr = new XMLHttpRequest();
    xhr.open('POST', url, true);
    xhr.onreadystatechange = function() { // Call a function when the state changes.
      if (xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) {
        resolve(JSON.parse(this.responseText));
      }
    };
    xhr.send();
  });
}


const DEFAULT_CONFIG: Configuration = {
  AvailableContainers: [
    {
      Type: 'Container',
      Width: 75,
      Height: 100,
      Style: {
        fillOpacity: 0,
        stroke: 'green'
      }
    }
  ],
  AvailableSymbols: [],
  MainContainer: {
    Type: 'Container',
    Width: 2000,
    Height: 100,
    Style: {
      fillOpacity: 0,
      stroke: 'black'
    }
  }
}
