import { StoreMissingError } from '../../domain/errors/StoreMissingError'
import type { InstalledStore } from '../../domain/models/InstalledStore'
import type { InstalledStoreRepository } from '../../domain/ports/InstalledStoreRepository'
import type { PreferencesService } from './PreferencesService'

/**
 * Which store is open.
 *
 * A machine can carry several: two catalogs, or the same catalog installed twice.
 * The remembered one wins, so the window reopens where it was left; the current
 * store is cached because every catalog call needs it and re-scanning the disk per
 * call would be silly.
 */
export class StoreSelectionService {
  private current: InstalledStore | null = null

  public constructor (
    private readonly stores: InstalledStoreRepository,
    private readonly preferences: PreferencesService
  ) {}

  public listStores (): readonly InstalledStore[] {
    return this.stores.findAll()
  }

  /** The store to drive, remembering the choice across runs. Null when there is none. */
  public findCurrentStore (): InstalledStore | null {
    const known = this.stores.findAll()
    const preferredHome = this.preferences.readStoreHome()
    const remembered = preferredHome === null
      ? undefined
      : known.find((store: InstalledStore): boolean => store.home === preferredHome)
    this.current = remembered ?? known[0] ?? null
    return this.current
  }

  public requireCurrentStore (): InstalledStore {
    const store = this.current ?? this.findCurrentStore()
    if (store === null) throw new StoreMissingError()
    return store
  }

  /** The store at this home, or an error naming it. Does not change what is open. */
  public requireStoreAt (home: string): InstalledStore {
    const store = this.stores.findByHome(home)
    if (store === null) throw new StoreMissingError(home)
    return store
  }

  public selectStore (home: string): InstalledStore {
    const store = this.stores.findByHome(home)
    if (store === null) throw new StoreMissingError(home)
    this.current = store
    this.preferences.updateStoreHome(store.home)
    return store
  }

  /** Adopt a store that was just installed, without a disk scan. */
  public adoptStore (store: InstalledStore): InstalledStore {
    this.current = store
    this.preferences.updateStoreHome(store.home)
    return store
  }

  /**
   * Forget a store that is no longer on the machine.
   *
   * The next store is not chosen here: `findCurrentStore` re-reads the disk and applies
   * the same rule it always does, so "which store is open" has exactly one answer in
   * one place. Clearing the remembered home first is what stops it choosing the one
   * that has just been deleted.
   */
  public forgetStore (store: InstalledStore): void {
    if (this.preferences.readStoreHome() === store.home) this.preferences.forgetStoreHome()
    if (this.current?.home === store.home) this.current = null
    this.findCurrentStore()
  }

  public readDefaultStoreRoot (): string {
    return this.stores.readRoots()[0] ?? ''
  }
}
