import {
  HttpClient,
  HttpContext,
  HttpErrorResponse,
  HttpHeaders,
  HttpParams
} from '@angular/common/http'
import { Injectable } from '@angular/core'
import { catchError, Observable, throwError } from 'rxjs'
import { Collection } from '../viya-api-explorer/models/collection.model'
import { AppStoreService } from './app-store.service'
import { ViyaApis } from '../viya-api-explorer/models/viya-apis.models'
import { ViyaApiFolderMembers } from '../viya-api-explorer/models/viya-api-folder-content.model'
import { ViyaApiFolder } from '../viya-api-explorer/models/viya-api-folder.model'
import { ViyaApiIdentities } from '../viya-api-explorer/models/viya-api-identities.model'
import { ViyaApiCurrentUser } from '../viya-api-explorer/models/viya-api-current-user.model'
import { ViyaComputeContexts } from '../viya-api-explorer/models/viya-compute-contexts.model'
import { ComputeContextDetails } from '../viya-api-explorer/models/viya-compute-context-details.model'

@Injectable({
  providedIn: 'root'
})
export class SasViyaService {
  // Viya APIs
  public viyaApis: ViyaApis = {
    Analytics_Insights: {
      insights: '/insights'
    },
    Visualisation: {
      reports: '/reports',
      reportImages: '/reportImages',
      reportTransforms: '/reportTransforms',
      visualAnalytics: '/visualAnalytics'
    },
    Compute: {
      jobs: '/jobDefinitions',
      jobExecution: '/jobExecution',
      contexts: '/compute/contexts'
    },
    Decision_Management: {
      modelManagement: '/modelManagement',
      modelRepository: '/modelRepository',
      modelPublish: '/modelPublish',
      microanalyticScore: '/microanalyticScore',
      dataMining: '/dataMining',
      businessRules: '/businessRules',
      referenceData: '/referenceData',
      treatmentDefinitions: '/treatmentDefinitions',
      subjectContacts: '/subjectContacts',
      decisionsRuntimeBuilder: '/decisionsRuntimeBuilder'
    },
    Core_Services: {
      folders: '/folders',
      files: '/files',
      annotations: '/annotations',
      authorization: '/authorization',
      relationships: '/relationships',
      SASLogon: '/SASLogon'
    },
    Automated_Machine_Learning: {
      mlPipelineAutomation: '/mlPipelineAutomation'
    },
    Other: {
      identities: '/identities'
    }
  }

  private serverUrl: string = ''

  constructor(
    private http: HttpClient,
    private appStoreService: AppStoreService
  ) {}

  /**
   * This function is replacing the constructor.
   * The reason for this is timing issues, other services eg. sas.service, app-store.service
   * must be initialized before this bit of code is executed.
   * This function is being called by `sas.service`
   */
  setup() {
    const adapterConfig = this.appStoreService.getDcAdapterSettings()

    this.serverUrl = adapterConfig?.serverUrl || ''

    // example collection request
    // this.getByCollection('jobs').subscribe((res) => {
    //   console.log('res', res)
    // })
  }

  /**
   *
   * @returns All Viya API collections that we support
   */
  getAllCollections() {
    return this.viyaApis
  }

  /**
   * Used for clicking trough collections / apis
   * @param url collection/api endpoint url
   * @returns
   */
  getByUrl(url: string): Observable<Collection> {
    return this.get<Collection>(`${this.serverUrl}${url}`, {
      withCredentials: true
    })
  }

  /**
   * We refer collection as starting point /jobs /folders etc.
   * @param apiCollection
   * @returns
   */
  getByCollection(apiCollection: string): Observable<Collection> {
    return this.get<Collection>(`${this.serverUrl}${apiCollection}`, {
      withCredentials: true
    })
  }

  getComputeContexts(): Observable<ViyaComputeContexts> {
    return this.get<ViyaComputeContexts>(
      `${this.serverUrl}/compute/contexts?limit=1000`,
      {
        withCredentials: true
      }
    )
  }

  getComputeContextById(id: string): Observable<ComputeContextDetails> {
    return this.get<ComputeContextDetails>(
      `${this.serverUrl}/compute/contexts/${id}`,
      {
        withCredentials: true
      }
    )
  }

  /**
   * @param path Path to the folder
   * @returns The folder info object
   */
  getFolderByPath(path: string): Observable<ViyaApiFolder> {
    return this.get<ViyaApiFolder>(
      `${this.serverUrl}/folders/folders/@item?path=${path}`,
      {
        withCredentials: true
      }
    )
  }

  getFolderMembers(folderId: string): Observable<ViyaApiFolderMembers> {
    return this.get<ViyaApiFolderMembers>(
      `${this.serverUrl}/folders/folders/${folderId}/members`,
      {
        withCredentials: true
      }
    )
  }

  getAdminGroups(limit: number = 5000): Observable<ViyaApiIdentities> {
    return this.get<ViyaApiIdentities>(
      `${this.serverUrl}/identities/groups?sortBy=name&limit=${limit}`,
      {
        withCredentials: true
      }
    )
  }

  getCurrentUser(): Observable<ViyaApiCurrentUser> {
    return this.get<ViyaApiCurrentUser>(
      `${this.serverUrl}/identities/users/@currentUser`,
      {
        withCredentials: true
      }
    )
  }

  get<T>(
    url: string,
    options?: {
      headers?:
        | HttpHeaders
        | {
            [header: string]: string | string[]
          }
      context?: HttpContext
      observe?: 'body'
      params?:
        | HttpParams
        | {
            [param: string]:
              | string
              | number
              | boolean
              | ReadonlyArray<string | number | boolean>
          }
      reportProgress?: boolean
      responseType?: 'json'
      withCredentials?: boolean
      transferCache?:
        | {
            includeHeaders?: string[]
          }
        | boolean
    }
  ): Observable<T> {
    return this.http.get<T>(url, options).pipe(
      catchError((err: HttpErrorResponse) => {
        console.log('url', url)
        console.log('err.status', err.status)
        if (err.status === 449 || err.status === 401) {
          // Retry once if we got a 449
          return this.http.get<T>(url, options)
        }
        // Otherwise propagate the error
        return throwError(() => err)
      })
    )
  }
}
