import {
  ChangeDetectorRef,
  Component,
  ElementRef,
  ViewEncapsulation
} from '@angular/core'
import { Router } from '@angular/router'
import { VERSION } from '../environments/version'
import { ActivatedRoute } from '@angular/router'
import { Location } from '@angular/common'
import '@clr/icons'
import '@clr/icons/shapes/all-shapes'
import { globals } from './_globals'
import { parseEmbedParam } from './shared/utils/parse-embed-param'
import moment from 'moment'
import { EventService } from './services/event.service'
import { AppService } from './services/app.service'
import { InfoModal } from './models/InfoModal'
import { DcAdapterSettings } from './models/DcAdapterSettings'
import { AppStoreService } from './services/app-store.service'
import { LicenceService } from './services/licence.service'
import '@cds/core/icon/register.js'
import {
  ClarityIcons,
  exclamationTriangleIcon,
  moonIcon,
  processOnVmIcon,
  sunIcon,
  tableIcon,
  trashIcon
} from '@cds/core/icon'

ClarityIcons.addIcons(
  moonIcon,
  sunIcon,
  exclamationTriangleIcon,
  tableIcon,
  trashIcon,
  processOnVmIcon
)

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss'],
  encapsulation: ViewEncapsulation.None,
  standalone: false
})
export class AppComponent {
  private dcAdapterSettings: DcAdapterSettings | undefined
  public commitVer: string
  public version: any
  public routeUrl: any
  public errTop: boolean | undefined
  public licenseExpiringDays: number | null = null
  public sasjsAborts: InfoModal[] = []

  public editorActive: boolean = false
  public approveActive: boolean = false
  public freeTierBanner: boolean = this.licenceService.isAppFreeTier.value
  public licenceProblem = this.licenceService.licenceProblem
  public appOverCapacity: boolean = false
  public appActive: boolean | null = null
  public requestsModal: boolean = false
  public showRegistration: boolean = true
  public startupDataLoaded: boolean = false
  public demoLimitNotice: { open: boolean; featureName: string } = {
    open: false,
    featureName: ''
  }

  public syssite = this.appService.syssite
  public licenceState = this.licenceService.licenceState
  public embed = globals.embed

  constructor(
    private appService: AppService,
    private licenceService: LicenceService,
    public router: Router,
    private route: ActivatedRoute,
    private location: Location,
    private eventService: EventService,
    private appStoreService: AppStoreService,
    private cdr: ChangeDetectorRef,
    private elementRef: ElementRef
  ) {
    this.parseDcAdapterSettings()

    /**
     * Prints app info in the console such as:
     * - Adapter versions
     * - App version
     * - Build timestamp
     *
     */
    ;(window as any).appinfo = () => {
      console.table({
        'Adapter version': VERSION.adapterVersion || 'n/a',
        'App version': (VERSION.tag || '').replace('v', ''),
        'Build timestamp': moment(parseInt(VERSION.timestamp)).format(
          'DD-MMM-YYYY HH:MM'
        ),
        '...': '...'
      })
    }

    this.subscribeToLicenseEvents()

    /**
     * Fetches git tag ang git hash from `version.ts` file
     * It's placed in the user drop down.
     */
    this.commitVer = (VERSION.tag || '').replace('v', '') + '.' + VERSION.hash

    router.events.subscribe((val) => {
      this.routeUrl = this.router.url

      if (typeof this.routeUrl !== 'undefined' && this.routeUrl.length > 4) {
        let rootParam = this.routeUrl.split('/')[1]

        if (rootParam === 'editor') {
          this.errTop = true
          this.editorActive = true
          this.approveActive = false
        } else if (rootParam === 'home') {
          this.errTop = false
          this.editorActive = true
          this.approveActive = false
        } else {
          this.errTop = true
          this.editorActive = false
        }

        globals.rootParam = rootParam
      }

      if (typeof this.routeUrl !== 'undefined' && this.routeUrl.length > 6) {
        if (this.routeUrl.includes('approveDet')) {
          this.approveActive = true
        } else if (this.routeUrl.includes('toapprove')) {
          this.approveActive = true
        } else {
          this.approveActive = false
        }
      }
    })

    const hashQuery = window.location.hash.split('?')[1]
    if (hashQuery && new URLSearchParams(hashQuery).get('embed') !== null) {
      const embed = parseEmbedParam(hashQuery)
      globals.embed = embed
      this.embed = embed
    }

    this.subscribeToShowAbortModal()
    this.subscribeToRequestsModal()
    this.subscribeToStartupData()
    this.subscribeToAppActive()
    this.subscribeToDemoLimitModal()

    /**
     * In Viya streaming apps, content is served within an iframe.  This code
     * makes that iframe "full screen" so it looks like a regular window.
     */
    if (window.frameElement) {
      window.frameElement.setAttribute(
        'style',
        'height:100%;width:100%;position:absolute'
      )
      window.frameElement.setAttribute('allowfullscreen', '')
      window.frameElement.setAttribute('frameborder', '0')
      window.frameElement.setAttribute('marginheight', '0')
      window.frameElement.setAttribute('marginwidth', '0')
      window.frameElement.setAttribute('scrolling', 'auto')
      window.focus()
    }
  }

  /**
   * Parses adapter settings that are found in the <sasjs> tag inside index.html
   */
  private parseDcAdapterSettings() {
    const sasjsElement = document.querySelector('sasjs')

    if (!sasjsElement) {
      this.licenceService.deactivateApp()
      setTimeout(() => {
        this.eventService.showAbortModal(
          null,
          "Please make sure 'SASJS' tag with config attributes is added to index.html",
          null,
          'SASjs Config not found'
        )
      })
      return
    }

    const getAppAttribute = (attribute: string) =>
      sasjsElement.getAttribute(attribute) || undefined

    const dcAdapterSettings = {
      serverUrl: getAppAttribute('serverUrl') || '',
      appLoc: getAppAttribute('appLoc') || '',
      serverType: getAppAttribute('serverType'),
      loginMechanism: getAppAttribute('loginMechanism') || '',
      adminGroup: getAppAttribute('adminGroup') || '',
      dcPath: getAppAttribute('dcPath') || '',
      debug: getAppAttribute('debug') === 'true' || false,
      useComputeApi: this.parseComputeApi(getAppAttribute('useComputeApi')),
      runAsTask: getAppAttribute('runAsTask') === 'true' || false,
      contextName: getAppAttribute('contextName') || '',
      hotLicenceKey: getAppAttribute('hotLicenceKey') || ''
    }

    this.dcAdapterSettings = dcAdapterSettings as any
    this.appStoreService.setDcAdapterSettings(dcAdapterSettings as any)
    this.appService.sasServiceInit()
  }

  /**
   * Opens licence page with the active licence problem
   * Problem details are encoded in the url
   */
  public licenceProblemDetails(url: string) {
    this.router.navigateByUrl(url)
  }

  /**
   * Based on string provided we return true, false or null
   * True -> Compute API
   * False -> JES API
   * Null -> JES WEB
   * @param value provided in the html <sasjs> tag
   * @returns true, false or null
   */
  private parseComputeApi(value: string | undefined): boolean | null {
    if (value === undefined) return null

    if (value === 'undefined' || value === 'null') return null

    return value === 'true' || false
  }

  /**
   * Listens for an `demo limit` event that will show the `Feature locked modal`
   * For exmaple when in editor upload feature is not enabled
   * When user tries to upload the excel, editor component will trgger this event
   * And stop the execution of file upload code.
   */
  public subscribeToDemoLimitModal() {
    this.eventService.onDemoLimitModalShow.subscribe((featureName: string) => {
      this.demoLimitNotice = {
        open: true,
        featureName
      }
    })
  }

  /**
   * Listens for licence events so banner can be displayed.
   * App is free tier, licence will expire, is expired or is invalid
   */
  public subscribeToLicenseEvents() {
    this.licenceService.isAppFreeTier.subscribe((isAppFreeTier: boolean) => {
      this.freeTierBanner = isAppFreeTier
    })

    this.licenceService.licenseExpiresInDays.subscribe(
      (licenseExpiringDays: number | null) => {
        if (licenseExpiringDays && licenseExpiringDays <= 14)
          this.licenseExpiringDays = licenseExpiringDays
      }
    )

    this.licenceService.isAppOverCapacity.subscribe(
      (isAppOverAppCapacity: boolean) => {
        this.appOverCapacity = isAppOverAppCapacity
      }
    )
  }

  /**
   * Listens for an event that will activate od deactivate full application.
   * Based on licence key prcoessing result
   */
  public subscribeToAppActive() {
    this.licenceService.isAppActivated.subscribe((value: any) => {
      this.appActive = value
    })
  }

  /**
   * Listnes to an event that is fired when showing abort modal
   * Then pushes object to array and modal is displayed
   * `abortId` is calculated and assigned so that modal can be removed and closed
   * it's an incrementing number
   */
  public subscribeToShowAbortModal() {
    this.eventService.onShowAbortModal.subscribe((sasjsAbort: InfoModal) => {
      let abortId = this.sasjsAborts.length + 1
      sasjsAbort.id = abortId
      this.sasjsAborts.push(sasjsAbort)
      this.cdr.detectChanges() //Changes were not triggered while hot is focused
    })
  }

  /**
   * When startupservice request is finished with valid response, this event will
   * make sure loading screen is gone.
   */
  public subscribeToStartupData() {
    this.eventService.onStartupDataLoaded.subscribe(() => {
      this.startupDataLoaded = true
    })
  }

  /**
   * Opens requests modal when requested from event service
   */
  public subscribeToRequestsModal() {
    this.eventService.onRequestsModalOpen.subscribe((value: boolean) => {
      this.requestsModal = true
    })
  }

  /**
   * Closes abort modal with matching ID (there could be multiple abort modals open)
   */
  public closeAbortModal(abortId: number) {
    let abortIndex = this.sasjsAborts.findIndex((abort) => abort.id === abortId)
    this.sasjsAborts.splice(abortIndex, 1)
  }

  /**
   * Toggles sidebar when requested from event service
   */
  public toggleSidebar() {
    this.eventService.toggleSidebar()
  }

  /**
   * Whether or not current route includes the route from param
   * @param route route to check
   */
  public isMainRoute(route: string): boolean {
    return this.router.url.includes(route)
  }

  /**
   * Opens a page for updating the licence.
   */
  public openLicencingPage() {
    this.router.navigateByUrl('/licensing/update')
  }

  /** "lib.table" of the last-opened editor table (for the VA embed back button). */
  get vaEditorLibds(): string {
    const e = globals.editor
    return e?.library && e?.table ? `${e.library}.${e.table}` : ''
  }

  /**
   * VA embed only: returns to the editor for the last-opened table. Lets users
   * who land on the stage/approve/review pages get back without knowing DC's
   * page paths. `globals.embed` (set at app init) keeps the editor in VA mode.
   */
  public backToEditor(): void {
    if (this.vaEditorLibds) {
      this.router.navigate(['/editor/' + this.vaEditorLibds], {
        queryParams: { embed: 'va' }
      })
    }
  }
}
