/*
 * Copyright (C) 2023 StApps
 * This program is free software: you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation, version 3.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program. If not, see <https://www.gnu.org/licenses/>.
 */
import {Component, ContentChild, EventEmitter, Input, OnInit, Output, TemplateRef} from '@angular/core';
import {ActivatedRoute, Router} from '@angular/router';
import {ModalController} from '@ionic/angular/standalone';
import {LangChangeEvent, TranslateService} from '@ngx-translate/core';
import {SCLanguageCode, SCSaveableThing, SCThings, SCUuid} from '@openstapps/core';
import {DataProvider, DataScope} from '../data.provider';
import {FavoritesService} from '../../favorites/favorites.service';
import {take} from 'rxjs/operators';
import {Network} from '@capacitor/network';
import {DataListContext} from '../list/data-list.component';

export interface ExternalDataLoadEvent {
  uid: SCUuid;
  forceReload: boolean;
  resolve: (item: SCThings | null | undefined) => void;
}

/**
 * A Component to display an SCThing detailed
 */
@Component({
  selector: 'stapps-data-detail',
  styleUrls: ['data-detail.scss'],
  templateUrl: 'data-detail.html',
})
export class DataDetailComponent implements OnInit {
  /**
   * The associated item
   *
   * undefined if not loaded, null when unavailable
   */
  item?: SCThings | null = undefined;

  collapse = false;

  @Input() inputItem?: SCThings;

  @Input() isModal = false;

  @Input() autoRouteDataPath = true;

  /**
   * The language of the item
   */
  language: SCLanguageCode;

  /**
   * Indicating wether internet connectivity is given or not
   */
  isDisconnected: Promise<boolean>;

  @ContentChild(TemplateRef) contentTemplateRef: TemplateRef<DataListContext<SCThings>>;

  @Input() externalData = false;

  /**
   * This is kind of a stupid situation where we would
   * like to use the default header in overriding elements
   * such as the assessment detail page, however the ionic
   * back button will not work if the header is in a subcomponent
   * which then means we have to copy and paste the header from
   * here into the overriding component.
   */
  @Input() defaultHeader = true;

  @Output() loadItem: EventEmitter<ExternalDataLoadEvent> = new EventEmitter<ExternalDataLoadEvent>();

  /**
   * Type guard for SCSavableThing
   */
  static isSCSavableThing(thing: SCThings | SCSaveableThing): thing is SCSaveableThing {
    return (thing as SCSaveableThing).data !== undefined;
  }

  constructor(
    protected readonly route: ActivatedRoute,
    router: Router,
    private readonly dataProvider: DataProvider,
    private readonly favoritesService: FavoritesService,
    readonly modalController: ModalController,
    translateService: TranslateService,
  ) {
    this.inputItem = router.getCurrentNavigation()?.extras.state?.item;
    if (!this.inputItem?.origin) {
      // We received a ThingWithoutReferences.
      // This can happen, for example, when detail views use `inPlace` list items
      delete this.inputItem;
    }
    this.language = translateService.currentLang as SCLanguageCode;
    translateService.onLangChange.subscribe((event: LangChangeEvent) => {
      this.language = event.lang as SCLanguageCode;
    });

    this.isDisconnected = new Promise(async resolve => {
      const isConnected = (await Network.getStatus()).connected;
      resolve(!isConnected);
    });
  }

  /**
   * Provides data item with given UID
   * @param uid Unique identifier of a thing
   * @param forceReload Indicating whether cached data should be ignored
   */
  async getItem(uid: SCUuid, forceReload: boolean) {
    try {
      const item =
        this.inputItem ??
        (await (this.externalData
          ? new Promise<SCThings | null | undefined>(resolve =>
              this.loadItem.emit({uid, forceReload, resolve}),
            )
          : this.dataProvider.get(uid, DataScope.Remote)));

      this.item = item
        ? // eslint-disable-next-line unicorn/no-null
          DataDetailComponent.isSCSavableThing(item)
          ? item.data
          : item
        : // eslint-disable-next-line unicorn/no-null
          null;
    } catch {
      // eslint-disable-next-line unicorn/no-null
      this.item = null;
    }
  }

  async ngOnInit() {
    const uid = this.route.snapshot.paramMap.get('uid') || '';
    await this.getItem(uid ?? '', false);
    // fallback to the saved item (from favorites)
    if (this.item === null) {
      this.favoritesService
        .get(uid)
        .pipe(take(1))
        .subscribe(item => {
          if (item !== undefined) {
            this.item = item.data;
          }
        });
    }
  }
}
