import { R } from '../../libs';
import { IS_BROWSER } from '../../constants';
import {
  ICookieAttributes,
  CookieValue,
  ICookieServerContext,
  CookieProperty,
} from './types';
import { prop } from './prop';

const SERVER_COOKIE_KEY = '__TDB_TEMP__';

export interface IServerCookie {
  key: string;
  value: CookieValue;
  options?: ICookieAttributes;
}

/**
 * A cookie builder for writing on the server.
 * Adds a set of values that is then passed to the client
 * in a `Set-Cookie` header.
 *
 * For example usage of cookies on the server see:
 *    Module:   @tdb/web
 *    Path:     /example/components/Home
 *    Method:   getInitialProps
 *
 */
export class ServerCookie {
  public cookies: IServerCookie[] = [];

  /**
   * Chaining method to add cookies.
   */
  public add(prop: CookieProperty<any>, value?: CookieValue) {
    if (value) {
      const { key } = prop;
      const options = R.isEmpty(prop.options) ? undefined : prop.options;
      this.cookies = [...this.cookies, { key, value, options }];
    }
    return this;
  }

  public get header() {
    const obj: any = {};
    this.cookies.forEach(cookie => (obj[cookie.key] = cookie));
    const json = JSON.stringify(obj);
    return `${SERVER_COOKIE_KEY}=${encodeURIComponent(json)}`;
  }

  /**
   * Writes and flushes the server cookie cache.
   */
  public write(ctx: ICookieServerContext) {
    if (!IS_BROWSER && this.cookies.length > 0) {
      const header = this.header;
      ctx.res.setHeader('Set-Cookie', header);
    }
  }

  /**
   * Removes all cookies values that have been added.
   */
  public clear() {
    this.cookies = [];
  }
}

/**
 * Write any cookies sent from the server.
 */
if (IS_BROWSER) {
  const serverCookies = prop(SERVER_COOKIE_KEY, { path: '/' });

  // Read the value from the server
  const cookies = serverCookies();
  if (cookies) {
    Object.keys(cookies)
      .map(key => cookies[key])
      .forEach(cookie => prop(cookie.key, cookie.options)(cookie.value));
  }

  // Clear temporary cookie.
  serverCookies(null);
}
