import { createHmac } from 'crypto';

import type {
  MediaProvider,
  MediaPublishCredentials,
  MediaSessionCreateInput,
  MediaSessionCreateResult,
  MediaSubscribeCredentials,
} from '../types';

const secret = process.env.BETTER_AUTH_SECRET;

if (!secret) {
  throw new Error('BETTER_AUTH_SECRET is required for mock media provider token signing');
}

const DEFAULT_TTL_SECONDS = 60 * 10;

const signToken = (payload: Record<string, unknown>, ttlSeconds = DEFAULT_TTL_SECONDS): { token: string; expiresInSeconds: number } => {
  const body = {
    ...payload,
    exp: Math.floor(Date.now() / 1000) + ttlSeconds,
  };

  const encoded = Buffer.from(JSON.stringify(body), 'utf8').toString('base64url');
  const signature = createHmac('sha256', secret).update(encoded).digest('base64url');

  return {
    token: `${encoded}.${signature}`,
    expiresInSeconds: ttlSeconds,
  };
};

const getBaseUrl = (): string => process.env.MEDIA_MOCK_BASE_URL ?? process.env.BETTER_AUTH_URL ?? 'http://localhost:3000';

export class MockMediaProvider implements MediaProvider {
  name = 'mock';

  async createSession(input: MediaSessionCreateInput): Promise<MediaSessionCreateResult> {
    // SIMPLE_STREAMING bypasses provider-backed transport at runtime. This metadata
    // path is kept only for legacy endpoints and backwards compatibility.
    const mediaSessionId = `mock_${input.streamSessionId}`;
    const baseUrl = getBaseUrl();

    return {
      provider: this.name,
      mediaSessionId,
      publishUrl: `${baseUrl}/media/mock/publish/${mediaSessionId}`,
      subscribeUrl: `${baseUrl}/media/mock/subscribe/${mediaSessionId}`,
    };
  }

  async issuePublishCredentials(input: {
    mediaSessionId: string;
    cameraDeviceId: string;
    ownerUserId: string;
  }): Promise<MediaPublishCredentials> {
    const baseUrl = getBaseUrl();
    const { token, expiresInSeconds } = signToken({
      typ: 'publish',
      provider: this.name,
      mediaSessionId: input.mediaSessionId,
      cameraDeviceId: input.cameraDeviceId,
      ownerUserId: input.ownerUserId,
    });

    return {
      provider: this.name,
      mediaSessionId: input.mediaSessionId,
      publishToken: token,
      publishUrl: `${baseUrl}/media/mock/publish/${input.mediaSessionId}`,
      expiresInSeconds,
    };
  }

  async issueSubscribeCredentials(input: {
    mediaSessionId: string;
    viewerDeviceId: string;
    ownerUserId: string;
  }): Promise<MediaSubscribeCredentials> {
    const baseUrl = getBaseUrl();
    const { token, expiresInSeconds } = signToken({
      typ: 'subscribe',
      provider: this.name,
      mediaSessionId: input.mediaSessionId,
      viewerDeviceId: input.viewerDeviceId,
      ownerUserId: input.ownerUserId,
    });

    return {
      provider: this.name,
      mediaSessionId: input.mediaSessionId,
      subscribeToken: token,
      subscribeUrl: `${baseUrl}/media/mock/subscribe/${input.mediaSessionId}`,
      expiresInSeconds,
    };
  }
}
