// Share a project via URL hash — encodes the entire project state in the
// URL fragment so it works without a server. State is JSON-stringified,
// compressed with `CompressionStream`, and base64url encoded.
//
// Recipients can paste the URL into their browser and the editor will
// restore the project state from the hash.

import type { Clip, SubtitleCue, Track, SilenceSegment, SubtitleStyle, ExportSettings } from '@/types';

export interface ShareableProject {
  v: number;
  name: string;
  tracks: Track[];
  clips: Clip[];
  subtitles: SubtitleCue[];
  silenceSegments: SilenceSegment[];
  subtitleStyle: SubtitleStyle;
  exportSettings: ExportSettings;
  sharedAt: number;
}

const VERSION = 1;
const MAX_URL_LENGTH = 60_000;

/** Encode bytes → base64url (URL-safe). */
function bytesToB64Url(bytes: Uint8Array): string {
  let bin = '';
  for (let i = 0; i < bytes.length; i++) bin += String.fromCharCode(bytes[i]);
  return btoa(bin).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
}

function b64UrlToBytes(s: string): Uint8Array {
  const pad = '='.repeat((4 - (s.length % 4)) % 4);
  const b64 = (s + pad).replace(/-/g, '+').replace(/_/g, '/');
  const bin = atob(b64);
  const out = new Uint8Array(bin.length);
  for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);
  return out;
}

async function gzip(data: Uint8Array): Promise<Uint8Array> {
  if (typeof CompressionStream === 'undefined') return data;
  const cs = new CompressionStream('gzip');
  const writer = cs.writable.getWriter();
  writer.write(data as unknown as BufferSource);
  writer.close();
  const reader = cs.readable.getReader();
  const chunks: Uint8Array[] = [];
  // eslint-disable-next-line no-constant-condition
  while (true) {
    const { done, value } = await reader.read();
    if (done) break;
    if (value) chunks.push(value);
  }
  const total = chunks.reduce((s, c) => s + c.byteLength, 0);
  const out = new Uint8Array(total);
  let off = 0;
  for (const c of chunks) {
    out.set(c, off);
    off += c.byteLength;
  }
  return out;
}

async function gunzip(data: Uint8Array): Promise<Uint8Array> {
  if (typeof DecompressionStream === 'undefined') return data;
  const ds = new DecompressionStream('gzip');
  const writer = ds.writable.getWriter();
  writer.write(data as unknown as BufferSource);
  writer.close();
  const reader = ds.readable.getReader();
  const chunks: Uint8Array[] = [];
  // eslint-disable-next-line no-constant-condition
  while (true) {
    const { done, value } = await reader.read();
    if (done) break;
    if (value) chunks.push(value);
  }
  const total = chunks.reduce((s, c) => s + c.byteLength, 0);
  const out = new Uint8Array(total);
  let off = 0;
  for (const c of chunks) {
    out.set(c, off);
    off += c.byteLength;
  }
  return out;
}

/** Encode a shareable project into a URL-safe string. */
export async function encodeShareable(p: Omit<ShareableProject, 'v' | 'sharedAt'>): Promise<string> {
  const full: ShareableProject = {
    v: VERSION,
    sharedAt: Date.now(),
    ...p,
  };
  const json = JSON.stringify(full);
  const bytes = new TextEncoder().encode(json);
  const compressed = await gzip(bytes);
  return bytesToB64Url(compressed);
}

/** Decode a share string back into a project object. Throws on parse error. */
export async function decodeShareable(s: string): Promise<ShareableProject> {
  const bytes = b64UrlToBytes(s);
  const decompressed = await gunzip(bytes);
  const json = new TextDecoder().decode(decompressed);
  const obj = JSON.parse(json) as ShareableProject;
  if (obj.v !== VERSION) {
    throw new Error(`เวอร์ชันโปรเจกต์ไม่ตรงกัน (v${obj.v})`);
  }
  return obj;
}

/** Build a full shareable URL by combining origin + path + #data. */
export function buildShareUrl(origin: string, encoded: string): string {
  // Strip any existing hash and append ours.
  const base = origin.replace(/#.*$/, '');
  const url = `${base}#p=${encoded}`;
  if (url.length > MAX_URL_LENGTH) {
    throw new Error(
      `URL ยาวเกินไป (${url.length} chars, max ${MAX_URL_LENGTH}) — ลองลดจำนวนคลิปหรือ subtitle ก่อน`,
    );
  }
  return url;
}

/** Extract the share payload from a window.location.hash string. */
export function extractShareHash(hash: string): string | null {
  if (!hash) return null;
  const m = hash.match(/[#&]p=([^&]+)/);
  return m ? m[1] : null;
}