/*
 Data stores

 @copyright (c) Copyright 2013-2017 Dorian Pula
 @license AGPL v3
 @author Dorian Pula [dorian.pula@amber-penguin-software.ca]
 */

import 'isomorphic-fetch';
import {observable, computed, runInAction, action} from 'mobx';

import {UserInfo} from  './user_info';


class AppStatusStore {
  @observable version = '';

  @action
  async fetchAppVersion() {
    this.version = '';
    try {

      const appStatus = await fetch('/status');
      const appStatusJson = await appStatus.json();
      runInAction(() => {
        this.version = appStatusJson.version;
      });

    } catch (error) {
      console.log(`Error getting the status: ${JSON.stringify(error)}`);
      runInAction(() => {
        this.version = '???';
      });
    }
  }
}

class UserStore {
  // TODO: Clean up the store heavily!!!

  @observable fullName = UserInfo.getUserInfo('full_name') || 'Stranger';
  @observable token = UserInfo.getUserInfo('auth_token') || '';
  @observable loginMessage = '';

  @computed
  get isUserLoggedIn() {
    return this.token !== '';
  }

  @action
  logoutUser(message = '') {
    if (message !== '') {
      console.log(`Logging out because "${message}"`);
    }
    UserInfo.removeUserInfo('auth_token');
    UserInfo.removeUserInfo('full_name');
    this.fullName = 'Stranger';
    this.token = '';
    this.loginMessage = message;
  }

  @action
  async loginUser(username, password) {

    const response = await fetch('/auth', {
      method: 'POST',
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({username: username, password: password})
    });

    if (response.status !== 200 && response.status !== 401) {
      console.log(`Unable to login for ${username}`);
      this.logoutUser(`Unable to login "${username}".`);
    } else if (response.status === 401) {
      runInAction(() => {
        this.loginMessage = `Invalid credentials for "${username}".`;
      });
    }

    try {
      const json = await response.json();
      let authToken = Reflect.get(json, 'access_token');

      if (authToken !== undefined) {

        const user = Reflect.get(json, 'user');

        runInAction(() => {
          this.token = authToken;
          this.fullName = user.fullName;
          this.loginMessage = '';
          UserInfo.setUserInfo('auth_token', authToken);
          UserInfo.setUserInfo('full_name', user.fullName);
        });

      }
    } catch (error) {
      console.log(`Error getting the status: ${JSON.stringify(error)}`);
      runInAction(() => {
        this.loginMessage = 'Error with login!';
      });
    }
  }
}

class SiteStore {

  // TODO: Migrate away from using this configuration object.
  @observable config = {
    logo: '/static/images/mr-penguin-amber.svg',
    tagline: 'Simple Site Scaffolding',
    footer: 'Rookeries - © 2013-2016 - Amber Penguin Software',
  };
  @observable name = 'Rookeries';
  @observable landingPage = '';

  // TODO: Break out components into blocks.
  @observable menu = [];
  @observable header = '';
  @observable footer = '';

  @action
  async fetchSiteInfo() {
    try {
      const response = await fetch('/api/site');
      const json = await response.json();
      runInAction(() => {
        this.config = json.config;
        this.name = json.name;
        this.landingPage = json.landingPage;
        this.menu = json.menu;
        this.footer = json.footer;
        this.header = json.header;
      });

    } catch (error) {
      // TODO: Add better error handling.
      console.error(`Failed to get site info: ${error}`);
    }
  }

  @action
  async saveSite() {
    // TODO: Make error handling more robust.
    // TODO: Add support for setting the landing page. (Or multiple slugs.)
    const content = {
      config: { ...this.config },
      menu: [...this.menu],
      name: this.name,
      landingPage: this.landingPage,
      header: this.header,
      footer: this.footer,
    };

    try {
      const response = await fetch(`/api/site`, {
        method: 'PUT',
        headers: {
          'Accept': 'application/json',
          'Content-Type': 'application/json',
          'Authorization': `JWT ${UserInfo.getUserInfo('auth_token')}`
        },
        body: JSON.stringify(content)
      });

      const json = await response.json();
      const siteContent = Reflect.get(json, 'content');
      if (siteContent === undefined) {
        throw Error();
      }

      await this.fetchSiteInfo();

    } catch (error) {
      // TODO: Add better error handling.
      console.error(`Unable to save page because of: ${error}`);
    }
  }
}

class PageStore {

  @observable content = '<h1><i>Loading...</i></h1>';
  @observable slug = '';
  @observable title = 'Loading...';


  @action
  async fetchPage(pageSlug) {
    try {
      const response = await fetch(`/api/pages/${pageSlug}`);
      const json = await response.json();

      const articleContent = Reflect.get(json, 'content');
      if (articleContent === undefined) {
        throw Error();
      }

      runInAction(() => {
        this.title = Reflect.get(json, 'title');
        this.content = articleContent;
        this.slug = pageSlug;
      });

    } catch (error) {
      // TODO: Add better error handling.
      console.error(`Unable to fetch page because of: ${error}`);
      runInAction(() => {
        this.slug = 'error';
        this.title = `Failed to fetch "${pageSlug}"`;
        this.content = `Unable to find content for "${pageSlug}"`;
      });
    }
  }

  @action
  async savePage(content) {
    // TODO: Figure out nicer article/block API.

    const pageSlug = content.slug;
    try {
      const response = await fetch(`/api/pages/${pageSlug}`, {
        method: 'POST',
        headers: {
          'Accept': 'application/json',
          'Content-Type': 'application/json',
          'Authorization': `JWT ${UserInfo.getUserInfo('auth_token')}`
        },
        body: JSON.stringify(content)
      });

      const json = await response.json();
      const articleContent = Reflect.get(json, 'content');
      if (articleContent === undefined) {
        throw Error();
      }

      runInAction(() => {
        this.title = Reflect.get(json, 'title');
        this.content = articleContent;
        this.slug = pageSlug;
      });

    } catch (error) {
      // TODO: Add better error handling.
      console.error(`Unable to save page because of: ${error}`);
      runInAction(() => {
        this.slug = 'error';
        this.title = `Failed to save "${pageSlug}"`;
        this.content = `Unable to save content for "${pageSlug}"`;
      });
    }
  }
}

export const appStatusStore = new AppStatusStore();
export const siteStore = new SiteStore();
export const pageStore = new PageStore();
export const userStore = new UserStore();

// TODO: Add in single root store?  See https://mobx.js.org/best/store.html
