import { BaseItemKind } from "@jellyfin/sdk/lib/generated-client/models";
import { z } from "zod";

import { fetchWithTrustedCertificatesAsync } from "@homarr/certificates/server";

import type { IntegrationTestingInput } from "../base/integration";
import { Integration } from "../base/integration";
import { TestConnectionError } from "../base/test-connection/test-connection-error";
import type { TestingResult } from "../base/test-connection/test-connection-service";
import type { IMediaServerIntegration } from "../interfaces/media-server/media-server-integration";
import type { CurrentSessionsInput, StreamSession } from "../interfaces/media-server/media-server-types";
import { convertJellyfinType } from "../jellyfin/jellyfin-integration";

const sessionSchema = z.object({
  NowPlayingItem: z
    .object({
      Type: z.nativeEnum(BaseItemKind).optional(),
      SeriesName: z.string().nullish(),
      Name: z.string().nullish(),
      SeasonName: z.string().nullish(),
      EpisodeTitle: z.string().nullish(),
      Album: z.string().nullish(),
      EpisodeCount: z.number().nullish(),
    })
    .optional(),
  Id: z.string(),
  Client: z.string().nullish(),
  DeviceId: z.string().nullish(),
  DeviceName: z.string().nullish(),
  UserId: z.string().optional(),
  UserName: z.string().nullish(),
});

export class EmbyIntegration extends Integration implements IMediaServerIntegration {
  private static readonly apiKeyHeader = "X-Emby-Token";
  private static readonly deviceId = "homarr-emby-integration";
  private static readonly authorizationHeaderValue = `Emby Client="Dashboard", Device="Homarr", DeviceId="${EmbyIntegration.deviceId}", Version="0.0.1"`;

  protected async testingAsync(input: IntegrationTestingInput): Promise<TestingResult> {
    const apiKey = super.getSecretValue("apiKey");
    const response = await input.fetchAsync(super.url("/emby/System/Ping"), {
      headers: {
        [EmbyIntegration.apiKeyHeader]: apiKey,
        Authorization: EmbyIntegration.authorizationHeaderValue,
      },
    });

    if (!response.ok) {
      return TestConnectionError.StatusResult(response);
    }

    return {
      success: true,
    };
  }

  public async getCurrentSessionsAsync(options: CurrentSessionsInput): Promise<StreamSession[]> {
    const apiKey = super.getSecretValue("apiKey");
    const response = await fetchWithTrustedCertificatesAsync(super.url("/emby/Sessions"), {
      headers: {
        [EmbyIntegration.apiKeyHeader]: apiKey,
        Authorization: EmbyIntegration.authorizationHeaderValue,
      },
    });

    if (!response.ok) {
      throw new Error(`Emby server ${this.integration.id} returned a non successful status code: ${response.status}`);
    }

    const result = z.array(sessionSchema).safeParse(await response.json());

    if (!result.success) {
      throw new Error(`Emby server ${this.integration.id} returned an unexpected response: ${result.error.message}`);
    }

    return result.data
      .filter((sessionInfo) => sessionInfo.UserId !== undefined)
      .filter((sessionInfo) => sessionInfo.DeviceId !== EmbyIntegration.deviceId)
      .filter((sessionInfo) => !options.showOnlyPlaying || sessionInfo.NowPlayingItem !== undefined)
      .map((sessionInfo): StreamSession => {
        let currentlyPlaying: StreamSession["currentlyPlaying"] | null = null;

        if (sessionInfo.NowPlayingItem) {
          currentlyPlaying = {
            type: convertJellyfinType(sessionInfo.NowPlayingItem.Type),
            name: sessionInfo.NowPlayingItem.SeriesName ?? sessionInfo.NowPlayingItem.Name ?? "",
            seasonName: sessionInfo.NowPlayingItem.SeasonName ?? "",
            episodeName: sessionInfo.NowPlayingItem.EpisodeTitle,
            albumName: sessionInfo.NowPlayingItem.Album ?? "",
            episodeCount: sessionInfo.NowPlayingItem.EpisodeCount,
          };
        }

        return {
          sessionId: `${sessionInfo.Id}`,
          sessionName: `${sessionInfo.Client} (${sessionInfo.DeviceName})`,
          user: {
            profilePictureUrl: super.url(`/Users/${sessionInfo.UserId}/Images/Primary`).toString(),
            userId: sessionInfo.UserId ?? "",
            username: sessionInfo.UserName ?? "",
          },
          currentlyPlaying,
        };
      });
  }
}
