import React, {ReactElement} from 'react';
import axios from "axios"
import ModalFeedback from "./ModalFeedback";
import ModalError from "./ModalError";
import ModalSuccess from './ModalSuccess';
import Screenshot from "./Screenshot";
import Recorder from "./Recorder";
import * as tus from 'tus-js-client'
import {ErrorBoundary} from "react-error-boundary";
import RecorderWhammy from "./RecorderWhammy";
import {withCookies} from 'react-cookie';
import {Feedback} from "./types/feedback";
import {openDB} from 'idb';
import {LoadingOverlay, Modal} from "@mantine/core";

class App extends React.Component<Feedback.Props, Feedback.State> {

  constructor(props: Feedback.Props) {
    super(props);

    if (!('url' in props.config) || typeof props.config.url !== 'string') {
      console.error('Feedback URL config param must be specified!')
    }

    if (!('tus' in props.config) || typeof props.config.tus !== 'string') {
      console.error('Feedback TUS config param must be specified!')
    }

    this.state = {
      show: 'feedback',
      screenshot: undefined,
      screenshotError: undefined,
      record: undefined,
      tusUniqIdRecord: undefined,
      tusUploadedRecord: false,
      tusUniqIdScreenshot: undefined,
      tusUploadedScreenshot: false,
      uploadProgress: 0,
      uploadProgressTusScreenshot: 0,
      newTaskUrl: undefined
    };

    if (FeedbackSupport) {
      FeedbackSupport.opened = true
    }
  }

  componentDidMount() {
    axios.get(`${this.props.config.url}/tasklists`, {
      params: {
        project: this.props.config.project
      },
      headers: this.props.config.headers || {}
    }).then((res) => {
      const tasklists: any = []
      res.data.data.tasklists.forEach((task: Api.Tasklist) => {
        tasklists.push({value: task.name, label: task.name})
      })

      this.setState({
        types: tasklists
      })
    }).catch((err) => {
      console.error(err)
      this.setState({
        types: []
      })
    })
  }

  componentDidUpdate(prevProps: Readonly<Feedback.Props>, prevState: Readonly<Feedback.State>, snapshot?: any) {
    if (this.state.show === '') {
      if (FeedbackSupport) {
        FeedbackSupport.opened = false
      }
    } else {
      if (FeedbackSupport) {
        FeedbackSupport.opened = true
      }
    }

    if (this.state.screenshot && !this.state.uploadProgressTusScreenshot && !this.state.tusUniqIdScreenshot) {
      this.uploadTusScreenshot()
    }
  }

  onClose = () => {
    this.setState({
      show: '',
      screenshot: undefined,
      record: undefined
    })
  }

  onSubmit = async (data: Feedback.Data) => {
    const self = this
    // if (self.state.record) {
    //   data.videoMimeType = self.state.record.mimeType
    //
    //   await self.uploadTus(data, 'Record')
    // } else {
    //   self.setState({
    //     tusUploadedRecord: true
    //   })
    // }
    //
    // if (self.state.record) {
    //
    // } else {
    //   self.setState({
    //     uploadProgressTus: undefined
    //   })
      self.uploadApi(data)
    // }
  }


  uploadTusScreenshot = () => {
    const self = this

    self.setState({
      uploadProgressTusScreenshot: 1
    })

    if (self.state.screenshot?.image) {
      const upload = new tus.Upload(self.state.screenshot?.blob, {
        endpoint: self.props.config.tus,
        retryDelays: [0, 3000, 5000, 10000, 20000],
        onError: function (error: Error) {
          console.error("Failed because: " + error)
          self.setState({
            show: 'error'
          })
        },
        onProgress: function (bytesUploaded: number, bytesTotal: number) {
          const percentage = bytesUploaded / bytesTotal * 100
          self.setState({
            uploadProgressTusScreenshot: percentage
          })
        },
        onSuccess: function () {
          const urlParts = upload.url?.split('/') || [];
          self.setState({
            tusUniqIdScreenshot: urlParts.length ? urlParts.pop() : undefined,
            tusUploadedScreenshot: true
          })
        }
      })

      // Check if there are any previous uploads to continue.
      upload.findPreviousUploads().then(function (previousUploads) {
        // Found previous uploads so we select the first one.
        if (previousUploads.length) {
          upload.resumeFromPreviousUpload(previousUploads[0])
        }

        // Start the upload
        upload.start()
      })
    }
  }

  uploadApi = async (data: Feedback.Data) => {
    if (data.assign?.length) {
      data.title = `${data.title} #assign:${data.assign}`
      if (data.assign) {
        delete(data['assign'])
      }
    }

    if (this.state.tusUploadedScreenshot) {
      data.tusUniqIdScreenshot = this.state.tusUniqIdScreenshot
    }

    try {
      const headers = this.props.config.headers || {}

      const indexedDb = await this.getIndexedDb()

      if (this.state.screenshotError !== undefined && this.state.screenshotError === 'failed') {
        data.description += '<br /><br /><h2>!!! SELHALO HTML2IMAGE při tvoření screenshotu !!!</h2>'
      }

      const formdata = {
        ...data,
        pageData: this.props.config.pageData || {},
        project: this.props.config.project || undefined,
        url: window.location.href,
        cookies: this.props.allCookies,
        localStorage: this.getLocalStorage(),
        indexedDb
      }

      const result = await axios.post<Api.NewTaskResponse>(this.props.config.url, formdata, {
        headers,
        onUploadProgress: progressEvent => {
          const progress = (progressEvent.loaded / (progressEvent.total || 1)) * 100;
          this.setState({
            uploadProgress: progress
          });
        }
      })

      console.log(result)

      this.setState({
        show: 'success',
        screenshot: undefined,
        record: undefined,
        uploadProgress: undefined,
        uploadProgressTusScreenshot: undefined,
        newTaskUrl: result.data.data.freeloLink
      })
    } catch (e) {
      console.error(e)

      this.setState({
        show: 'error',
        uploadProgress: undefined,
        uploadProgressTusScreenshot: undefined
      })
    }
  }

  onScreenshot = () => {
    this.setState({
      show: 'screenshot'
    })
  }

  onScreenshotSuccess = (data: Screenshot.Data) => {
    this.setState({
      show: 'feedback',
      screenshot: data,
      screenshotError: undefined
    })
  }

  onScreenshotError = (error: Error) => {
    this.setState({
      show: 'feedback',
      screenshot: undefined,
      screenshotError: 'failed'
    })

    if (error !== undefined) {
      console.error(error)
    }
  }

  onRecorder = () => {
    this.setState({
      show: 'recorder'
    })
  }

  getLocalStorage = () => {
    const data: { [key: string]: any } = {}
    for (let i: number = 0; i < localStorage.length; i++) {
      const key = localStorage.key(i)
      if (key !== null) {
        data[key] = localStorage.getItem(key)
      }
    }

    return data
  }

  getIndexedDb = async () => {
    const idbs = await indexedDB.databases()

    const allData: { [key: string]: { [key: string]: any } } = {}
    await Promise.all(
      idbs.map(async (dbInfo) => {
        if (dbInfo.name) {
          allData[dbInfo.name] = {}
          const db = await openDB(dbInfo.name)

          // @ts-ignore
          for (const name of db.objectStoreNames) {

            const keys = await db.transaction(name, 'readonly')
              .objectStore(name)
              .getAllKeys()

            allData[dbInfo.name][name] = {}
            await Promise.all(
              keys.map(async (key) => {
                // @ts-ignore
                allData[dbInfo.name][name][key] = await db.transaction(name, 'readonly')
                  .objectStore(name)
                  .get(key)
              })
            )
          }
        }
        ;
      })
    )

    return allData
  }

  render(): ReactElement | null {

    if (this.state.types === undefined) {
      return <Modal fullScreen opened={true} onClose={() => false}><LoadingOverlay visible /></Modal>
    } else {
      return (
        <>
          <ModalFeedback
            config={this.props.config}
            show={this.state.show === 'feedback'}
            onClose={this.onClose}
            onSubmit={this.onSubmit}
            onRecorder={this.onRecorder}
            screenshot={this.state.screenshot}
            screenshotError={this.state.screenshotError}
            record={this.state.record}
            onScreenshot={this.onScreenshot}
            uploadProgress={this.state.uploadProgress}
            uploadProgressTusScreenshot={this.state.uploadProgressTusScreenshot}
            tusUploadedScreenshot={this.state.tusUploadedScreenshot}
            recorderError={this.state.recorderError}
            types={this.state.types}
          />

          <ModalError
            show={this.state.show === 'error'}
            onClose={() => this.setState({
              show: 'feedback'
            })}
          />

          <ModalSuccess
            show={this.state.show === 'success'}
            taskUrl={this.state.newTaskUrl}
            onClose={() => this.setState({
              show: '',
              newTaskUrl: undefined
            })}
          />

          {this.state.show === 'screenshot' &&
              <ErrorBoundary
                  fallback={<></>}
                  onError={this.onScreenshotError}
              >
                  <Screenshot
                      onError={this.onScreenshotError}
                      onSuccess={this.onScreenshotSuccess}
                      config={this.props.config}
                  />
              </ErrorBoundary>
          }

          <ErrorBoundary
            fallback={<></>}
            onError={(error) => {
              this.setState({
                recorderError: error
              })
            }}
          >
            {false && <Recorder
                show={this.state.show === 'recorder'}
                setRecord={(record: Record.Data) => {
                  this.setState({
                      record: record,
                      show: 'feedback'
                    }
                  )
                }}
            />}
          </ErrorBoundary>

          {false && this.state.recorderError !== undefined && this.state.show === 'recorder' &&
              <RecorderWhammy
                  show={this.state.show === 'recorder'}
                  setRecord={(record: Record.Data) => {
                    this.setState({
                        record: record,
                        show: 'feedback'
                      }
                    )
                  }}
              />
          }
        </>
      )
    }
  }
}

export default withCookies<Feedback.Props>(App);
