import { Injectable } from '@angular/core';
import { RawYearCode, RawTermCode } from '../shared/term-codes/without-era';
import { YearCode } from '../shared/term-codes/yearcode';
import { TermCode, Era } from '../shared/term-codes/termcode';

@Injectable({ providedIn: 'root' })
export class TermCodeFactory {
  private state: 'uninitialized' | 'initialized' = 'uninitialized';
  public active: ReadonlyArray<TermCode> = [];

  private static pickEra(code: RawTermCode, active: ReadonlyArray<TermCode>) {
    const isActive = active.some(t => t.equals(code));
    if (isActive) {
      return Era.Active;
    }

    const beforeActive = active.every(t => t.comesAfter(code));
    if (beforeActive) {
      return Era.Past;
    } else {
      return Era.Future;
    }
  }

  private requireInitialization() {
    if (this.isNotInitialized()) {
      throw new Error('cannot use TermCodeFactory without active terms');
    }
  }

  public isNotInitialized(): boolean {
    return this.state !== 'initialized';
  }

  public setActiveTermCodes(active: RawTermCode[]) {
    if (this.isNotInitialized() === false) {
      throw new Error('the TermCodeFactory was already initialized');
    } else if (active.length === 0) {
      throw new Error('app cannot have 0 active terms, must have at least 1');
    }

    this.state = 'initialized';
    this.active = active
      .sort(RawTermCode.sort)
      .map(t => new TermCode(t, Era.Active, this.fromRawYearCode(t.yearCode)));
  }

  public first() {
    return this.active[0];
  }

  public fromString(str: string) {
    this.requireInitialization();

    const raw = new RawTermCode(str);
    const era = TermCodeFactory.pickEra(raw, this.active);
    return new TermCode(raw, era, this.fromRawYearCode(raw.yearCode));
  }

  public fromRawYearCode(raw: RawYearCode | string): YearCode {
    if (typeof raw === 'string') {
      raw = new RawYearCode(raw);
    }

    const fall = TermCodeFactory.pickEra(raw.fall(), this.active);
    const spring = TermCodeFactory.pickEra(raw.spring(), this.active);
    const summer = TermCodeFactory.pickEra(raw.summer(), this.active);
    return new YearCode(raw.toString(), fall, spring, summer);
  }

  public fromYear(year: YearCode) {
    return {
      fall: this.fromString(`${year.toString()}2`),
      spring: this.fromString(`${year.toString()}4`),
      summer: this.fromString(`${year.toString()}6`),
    };
  }
}