const useErrorBoundary = () => {
  const [error, setError] = React.useState(null);

  const ErrorBoundary = ({ children }) => {
    if (error) {
      return <div class="alert alert-danger" role="alert">
        <strong>Ошибка!</strong> {error.message}
      </div>
    }
    return children;
  };

  const throwError = (message) => {
    setError(new Error(message));
  };

  return { ErrorBoundary, throwError };
};

class App extends React.Component {
  setState() {
    const params = new Proxy(new URLSearchParams(window.location.search), {
      get: (searchParams, prop) => searchParams.get(prop),
    });
    this.videoUrl = params.video_url;
    if (this.videoUrl) {
      this.loggedIn = true;
    } else {
      this.loggedIn = false;
    }
  }

  componentWillMount() {
    this.setState();
  }

  render() {
    if (this.loggedIn) {
      return <MainPage videoUrl={this.videoUrl} />;
    }
    return <Home />;
  }
}

function getVideoComponent(video_id, seconds) {
  if (video_id == null) {
    return ""
  }
  var video_source = video_id.split("_")
  if (video_source[0] == 'vk') {
    var user_id = video_source[1]
    var video_id = video_source[2]
    var vk_url = "https://vk.com/video_ext.php?oid=" + user_id + "&id=" + video_id + "&hd=2&js_api=1"
    return (<iframe src={vk_url} title="player" frameborder="0" allowfullscreen="" width="853" height="480"></iframe>)
  }
  if (video_source[0] == 'youtube') {
    return <YoutubeEmbed embedId={video_source[1]} seconds={seconds} />;
  }
}

function Home() {
  const [url, setUrl] = React.useState("");
  const { ErrorBoundary, throwError } = useErrorBoundary();
  function sendUrl() {
    event.preventDefault();
    createTask(url)
  }
  function handleChange(event) {
    setUrl(event.target.value)
  }
  function createTask(url_s) {
    const requestOptions = {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ url: url_s })
    };
    fetch('/v1/tasks', requestOptions).then(response => {
      if (!response.ok) {
        if (response.status == 400) {
          throwError("Неправильный формат ссылки");
        }
        return Promise.reject(response);
      }
      return response.json();
    }).then(data => {
      var searchParams = new URLSearchParams(window.location.search);
      searchParams.set("video_url", url_s);
      window.location.search = searchParams.toString();
    }).catch(error => {
      if (typeof error.json === "function") {
        error
          .json()
          .then(jsonError => {
            if (jsonError.state == 6) {
              throwError(jsonError.result)
            }
            console.error("Json error from API");
            console.error(jsonError);
          })
          .catch(genericError => {
            console.error("Generic error from API");
            console.error(error.statusText);
          });
      } else {
        console.error("Fetch error");
        console.error(error);
      }
    });
  }

  return (
    <div>
      <Header />
      <div className="container pt-5 mx-auto">
        <div className="col-md-12 jumbotron text-center">
          <h1>СОKPAT</h1>
          <p>«Сократ: краткий пересказ видео — просто и эффективно».</p>

          <form method="post" onSubmit={sendUrl}>
            <div className="col-auto">
              <input
                type="text"
                value={url}
                onChange={handleChange}
                name="url"
                className="form-control" />
            </div>
            <ErrorBoundary />
            <br />
            <button type="submit" className="btn btn-primary btn-lg btn-login btn-block">Короче</button>
          </form>

        </div>
        <br />
        <div class="alert alert-warning" role="alert">
          <div>
            Привет! Сервис все еще в разработке, поэтому у нас есть несколько ограничений:
          </div>
          <ol class="list-group list-group-numbered list-group-flush">
            <li class="list-group-item list-group-item-warning">Сейчас работаем только с Youtube и VK Video</li>
            <li class="list-group-item list-group-item-warning">Видео должно быть короче 4 часов</li>
            <li class="list-group-item list-group-item-warning">Видео должно быть на русском языке</li>
            <li class="list-group-item list-group-item-warning">Видео не должно быть трансляцией</li>
          </ol>
        </div>
      </div>
    </div>
  )
}

function MainPage({ videoUrl }) {
  const [task, setTask] = React.useState({});
  const [taskID, setTaskID] = React.useState();

  React.useEffect(() => {
    const fetchAPI = async (videoUrl) => {
      const requestOptions = {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({ url: videoUrl })
      };
      await fetch('/v1/tasks', requestOptions).then(response => {
        if (!response.ok) {
          return Promise.reject(response);
        }
        return response.json();
      }).then(data => {
        setTaskID(data.task_id)
        fetchTaskUntilSucceeded(data.task_id, 1000)
      }).catch(error => {
        if (typeof error.json === "function") {
          error
            .json()
            .then(jsonError => {
              console.error("Json error from API");
              console.error(jsonError);
            })
            .catch(genericError => {
              console.error("Generic error from API");
              console.error(error.statusText);
            });
        } else {
          console.error("Fetch error");
          console.error(error);
        }
      });
    };
    fetchAPI(videoUrl);
  }, []);

  function logout() {
    window.history.pushState({}, document.title, window.location.pathname);
    location.reload();
  }

  function fetchTaskUntilSucceeded(taskID, retries) {
    if (retries <= 0) {
      throw new Error("No more retries")
    }
    const requestOptions = {
      method: 'GET',
      headers: {
        'Content-Type': 'application/json'
      }
    };
    fetch("/v1/tasks/" + taskID, requestOptions).then((res) => {
      if (!res.ok) {
        console.error('Error fetching task', res);
        return;
      }
      return res.json();
    }).then((result) => {
      if (result === undefined) {
        setTimeout(() => fetchTaskUntilSucceeded(taskID, retries - 1), 5000);
        return;
      }
      setTask(result.task);
      if (result.task.state < 4) {
        setTimeout(() => fetchTaskUntilSucceeded(taskID, retries - 1), 5000);
      }
    });
  }

  return (
    <div>
      <Header />
      <div className="container">
        <br />
        <span className="pull-right btn btn-primary">
          <a onClick={logout}>Вернуться</a>
        </span>
        <h2></h2>
        <div className="row">
          <div>
            <Task task={task} />
          </div>
        </div>
      </div>
    </div>
  );
}

function Header() {
  const [user, setUser] = React.useState("");
  const [userAuthenticated, setUserAuthenticated] = React.useState(false);
  const [isLoading, setIsLoading] = React.useState(false);
  React.useEffect(() => {
    const fetchData = async () => {
      fetch("/auth/info/")
        .then(resp => resp.json())
        .then(data => {
          console.log(data);
          setUserAuthenticated(data.is_authenticated);
          if (data.is_authenticated == true) {
            setUser(data.display_name);
          }
          setIsLoading(false);
        })
        .catch(err => {
          console.log("error", err);
          setIsLoading(false);
        });
    };

    fetchData();
  }, []);

  let userMarkup;
  if (isLoading) {
    userMarkup = (<div></div>);
  } else {
    if (userAuthenticated) {
      userMarkup = (
        <div class="dropdown text-end">
          <a href="#" class="d-block link-body-emphasis text-decoration-none dropdown-toggle" data-bs-toggle="dropdown"
            aria-expanded="false">
            {user}
          </a>
          <ul class="dropdown-menu text-small">
            <li><a class="dropdown-item" href="/auth/logout/">Выйти</a></li>
          </ul>
        </div>
      );
    } else {
      userMarkup = (
        <a href="/auth/" class="btn btn-outline-primary me-2" role="button">Войти</a>
      );
    }
  }

  return (
    <header>
      <div class="container">
        <div class="d-flex flex-wrap align-items-center justify-content-center justify-content-lg-start">
          <div class="nav col-12 col-lg-auto me-lg-auto mb-2 justify-content-center mb-md-0"></div>

          {userMarkup}
        </div>
      </div>
    </header>
  );
}

function Task({ task }) {
  const [seconds, setSeconds] = React.useState(0);
  function getTaskState(task) {
    switch (task.state) {
      case 0:
        return "Скачиваем ..."
      case 1:
        return "Распознаём ..."
      case 2:
        return "Пытаемся понять что распознали ..."
      case 3:
        return "Разобрались, резюмируем ..."
      case 6:
        if (task.result == "error downloading video: nonretriable\nwrong video type") {
          return "Пока что мы не умеем обрабатывать трансляции 😿"
        }
        if (task.result == "error downloading video: nonretriable\nvideo is too long") {
          return "Пока что мы не умеем работать с видео длиннее 4 часов 😿"
        }
        return "Что-то пошло не так 😿"
      default:
        return "Готово!"
    }
  }

  function setVideoSeconds(seconds) {
    setSeconds(seconds)
    document.getElementById("youtube-player").scrollIntoView({ behavior: 'smooth' })
  }
  return (
    <div className="col-md-12">
      <div className="panel panel-default">
        {task.url &&
          <div className="panel-heading">
            {getVideoComponent(task.video_id, seconds)} {!([4, 5].includes(task.state)) && <h6>{getTaskState(task)}</h6>}
          </div>
        }

        <div className="accordion panel-body col-md-10">
          {[4, 5].includes(task.state) && <div>
            <Questions task_id={task.id} />
            <div class="accordion-item">
              <h2 className="accordion-header display-6">
                <button
                  class="accordion-button"
                  type="button"
                  data-bs-toggle="collapse"
                  data-bs-target="#collapseOne"
                  aria-expanded="true"
                  aria-controls="collapseOne">
                  Резюме:
                </button>
              </h2>
              <div
                id="collapseOne"
                class="accordion-collapse collapse show"
                data-bs-parent="#accordionExample">
                <div class="accordion-body">
                  {task
                    .summarization
                    .topics
                    .map(summarization => <Summarization summarization={summarization} onTimestampClick={() => { setVideoSeconds(summarization.first_message_id) }} />)}
                </div>
              </div>
            </div>
          </div>
          }

          {task.state >= 2 && task.state < 6 && <div class="accordion-item">
            <h2 class="accordion-header">
              <button
                class="accordion-button collapsed"
                type="button"
                data-bs-toggle="collapse"
                data-bs-target="#collapseTwo"
                aria-expanded="false"
                aria-controls="collapseTwo">
                Транскрипция:</button>
            </h2>
            <div
              id="collapseTwo"
              class="accordion-collapse collapse"
              data-bs-parent="#accordionExample">
              <div class="accordion-body">
                {task
                  .transcription
                  .map(transcription => <Transcription transcription={transcription} />)}
              </div>
            </div>
          </div>
          }
        </div>
      </div>
    </div>
  )
}

function Transcription({ transcription }) {
  return (
    <div className="col-md-10">
      <div className="panel panel-default">
        <div className="panel-body">
          {transcription.message_id}: {transcription.text}
        </div>
      </div>
    </div>
  )
}

function Summarization({ summarization, onTimestampClick }) {
  function toTimestamp(seconds) {
    return new Date(seconds * 1000)
      .toISOString()
      .slice(11, 19);
  }
  return (
    <div className="col-md-10">
      <div className="panel panel-default">
        <div className="panel-body summary">
          <h4> <a href='javascript:void(0)' onClick={onTimestampClick}>{toTimestamp(summarization.first_message_id)}</a> {summarization.name}:<a /></h4>
          {summarization.summary}
        </div>
      </div>
    </div>
  )
}

function Questions({ task_id }) {
  const [question,
    setQuestion] = React.useState('');
  const [questions,
    setQuestions] = React.useState([]);
  const [isLoading,
    setIsLoading] = React.useState(false);

  function sendQuestion() {
    if (question != '') {
      event.preventDefault();
      setQuestions(prevState => ([
        ...prevState, {
          "message_id": 0,
          "message_dt": new Date(),
          "content": question,
          "role": "user"
        }
      ]))
      serverQuestionRequest(task_id, question);
      setQuestion('')
    }
  }

  function fetchAnswersUntilSucceeded(task_id, message_id, question_uuid, retries) {
    let url = '/v1/tasks/' + task_id + '/questions/' + message_id + '/?question_uuid=' + question_uuid
    if (retries <= 0) {
      throw new Error("No more retries")
    }
    fetch(url).then(res => {
      if (res.status == 200) {

        return res
          .json()
          .then(result => {
            setIsLoading(false)
            setQuestions(prevState => ([
              ...prevState,
              result[1]
            ]))
          }, error => {
            console.error(error)
          })
      }
      if ([202, 500].includes(res.status)) {
        setTimeout(() => {
          return fetchAnswersUntilSucceeded(task_id, message_id, question_uuid, retries - 1);
        }, 5000);
        return;
      }

      setIsLoading(false);
      setQuestions(prevState => ([
        ...prevState,
        {
          "message_id": 0,
          "message_dt": new Date(),
          "content": "Что-то пошло не так",
          "role": "warning"
        }
      ]))
    })
  }

  function serverQuestionRequest(task_id, question_s) {
    const requestOptions = {
      mode: 'no-cors',
      method: 'POST',
      headers: {
        'Content-Type': 'application/json; charset=utf-8'
      },
      body: JSON.stringify({ question: question_s })
    }
    let url = '/v1/tasks/' + task_id + '/questions'
    fetch(url, requestOptions)
      .then(res => {
        if (res.status == 200) {
          res
            .json()
            .then(result => {
              setIsLoading(true);
              fetchAnswersUntilSucceeded(task_id, result.message_id, result.question_uuid, 30);
            }, error => {
              setIsLoading(false);
              console.error(error);
            });
          return;
        }
        if (res.status == 409) {
          setQuestions(prevState => ([
            ...prevState,
            {
              "message_id": 0,
              "message_dt": new Date(),
              "content": "Что-то пошло не так, попробуйте еще раз",
              "role": "warning"
            }
          ]));
          return;
        }
        setQuestions(prevState => ([
          ...prevState,
          {
            "message_id": 0,
            "message_dt": new Date(),
            "content": "Что-то пошло не так",
            "role": "warning"
          }
        ]));
        return;
      })
  }

  return (
    <div className="col-md-12">
      <div className="item-container dialog">
        {questions.length > 0 && questions.map(q => <Answer answer={q} />)}
        {isLoading && <div
          style={{
            "background-color": "#b7f0dd",
            "padding": "5px 10px",
            "border-radius": "5px",
            "margin-bottom": "5px",
            "display": "inline-block",
            "text-align": "right",
            "max-width": "80%",
            "align-self": "flex-end"
          }}>Сократ печатает
          <Typewriter text=" . . . " delay={100} /></div>}
      </div>
      <form method="post" onSubmit={sendQuestion} disabled={isLoading}>
        <div className="col-auto">
          <span class="input-group-text" id="basic-addon1">
            <input
              aria-describedby="basic-addon1"
              type="text"
              value={question}
              onChange={e => setQuestion(e.target.value)}
              name="question"
              className="col-md-11" />
            <button className="col-md-1 btn" type="submit" disabled={isLoading}>
              <svg
                xmlns="http://www.w3.org/2000/svg"
                width="16"
                height="16"
                fill="currentColor"
                class="bi bi-send"
                viewBox="0 0 16 16">
                <path
                  d="M15.854.146a.5.5 0 0 1 .11.54l-5.819 14.547a.75.75 0 0 1-1.329.124l-3.178-4.995L.643 7.184a.75.75 0 0 1 .124-1.33L15.314.037a.5.5 0 0 1 .54.11ZM6.636 10.07l2.761 4.338L14.13 2.576zm6.787-8.201L1.591 6.602l4.339 2.76z" />
              </svg>
            </button>
          </span>
        </div>
      </form>
    </div>
  )
}

function YoutubeEmbed({ embedId, seconds }) {
  let link = "https://www.youtube.com/embed/" + embedId
  if (seconds > 0) {
    link = link + "?version=3&loop=1&enablejsapi=1&autoplay=1&start=" + seconds
  }
  return (
    <div id="youtube-player" className="col-md-10">
      <iframe
        width="853"
        height="480"
        src={link}
        frameBorder="0"
        allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
        allowFullScreen
        title="Embedded youtube" />
    </div>
  )
}

function Answer({ answer }) {
  let el_style
  if (answer.role == 'user') {
    el_style = {
      "background-color": "#e0dff5",
      "text-align": "left",
      "padding": "5px 10px",
      "margin-bottom": "5px",
      "border-radius": "5px",
      "max-width": "80%",
      "display": "inline-block"
    }
  } else if (answer.role == 'warning') {
    el_style = {
      "background-color": "#fc4903",
      "padding": "5px 10px",
      "border-radius": "5px",
      "margin-bottom": "5px",
      "display": "inline-block",
      "text-align": "right",
      "max-width": "80%",
      "align-self": "flex-end"
    }
  } else {
    el_style = {
      "background-color": "#b7f0dd",
      "padding": "5px 10px",
      "border-radius": "5px",
      "margin-bottom": "5px",
      "display": "inline-block",
      "text-align": "right",
      "max-width": "80%",
      "align-self": "flex-end"
    }
  }
  return (
    <div style={el_style}>
      {answer.content}
    </div>
  )
}
function Typewriter({ text, delay }) {
  const [currentText,
    setCurrentText] = React.useState('');
  const [currentIndex,
    setCurrentIndex] = React.useState(0);

  React.useEffect(() => {
    if (currentIndex < text.length) {
      const timeout = setTimeout(() => {
        setCurrentText(prevText => prevText + text[currentIndex]);
        setCurrentIndex(prevIndex => prevIndex + 1);
      }, delay);

      return () => clearTimeout(timeout);
    }
  }, [currentIndex, delay, text]);

  return <span>{currentText}</span>;
}

ReactDOM.render(
  <App />, document.getElementById('app'));
