import apiClient from "@/lib/axiosClient";
import { API_ENDPOINTS } from "@/lib/apiEndpoints";

/* ── Types ──────────────────────────────────────────────────────────────────── */

export interface Language { id: number; name: string; image: string; }
export interface PodcastItem { id: number; title: string; portrait_img: string; }
export interface ContentByChannelResponse { status: number; result: PodcastItem[]; }
export interface FeedContentResult { content_type: string; image: string; image_url: string; video: string; video_url: string; }

export type UploadProgressCallback = (pct: number, label: string) => void;

/* ── Chunked upload ─────────────────────────────────────────────────────────── */

const CHUNK_SIZE = 2 * 1024 * 1024; // 2 MB

async function uploadInChunks(
  endpoint: string,
  file: File,
  fields: Record<string, string | File | undefined>,
  onProgress?: UploadProgressCallback,
  progressLabel = "Uploading…",
  contentFieldName = "file",
): Promise<unknown> {
  const totalChunks = Math.max(1, Math.ceil(file.size / CHUNK_SIZE));
  let filePath = "";

  /* ── Phase 1: Upload binary chunks ────────────────────────────────────────
     Each chunk only carries the binary slice + directory.
     Intermediate responses: { status: 206 }
     Final chunk response:   { status: 200, result: { file_path: "vid_...mp4" } }
  ──────────────────────────────────────────────────────────────────────── */
  /* Guard: if file.size is a perfect multiple of CHUNK_SIZE, the slice for the
     last calculated chunk would be empty — skip it and let the previous chunk
     (which already delivered the full file) carry the completion signal.       */
  const actualChunks = totalChunks;       // remainder chunk exists — all needed (kept symmetrical)

  for (let i = 0; i < actualChunks; i++) {
    const start = i * CHUNK_SIZE;
    const end = Math.min(start + CHUNK_SIZE, file.size);
    if (start >= file.size) break; // safety: never send an empty chunk

    const chunk = file.slice(start, end);
    const form = new FormData();
    form.append("chunk_index", String(i));
    form.append("total_chunks", String(totalChunks));
    form.append("file", chunk, file.name);

    // Send directory so the server knows where to store each chunk
    const dir = fields.directory;
    if (typeof dir === "string" && dir) form.append("directory", dir);

    const res = await apiClient.post(endpoint, form, {
      headers: { "Content-Type": "multipart/form-data" },
    });

    onProgress?.(Math.round(((i + 1) / totalChunks) * 95), progressLabel);

    // Extract file_path from any known response shape
    const d = res.data as Record<string, unknown>;
    if (d?.status === 200) {
      const result = d.result as Record<string, unknown> | undefined;
      const fp =
        (typeof result?.file_path === "string" && result.file_path) ||
        (typeof d.file_path === "string" && d.file_path) ||
        "";
      if (fp) { filePath = fp; break; }
    }
  }

  if (!filePath) {
    throw new Error(
      `Upload failed: server did not return a file_path after ${totalChunks} chunk(s). ` +
      `Check the API response for the last chunk.`
    );
  }

  /* ── Phase 2: Finalize — submit metadata with server-assigned filename ──────
     `file` is now the string path returned by the server (e.g. "vid_...mp4"),
     NOT the binary blob. All other metadata fields (title, images, etc.)
     are sent in this single multipart POST.
  ──────────────────────────────────────────────────────────────────────── */
  onProgress?.(97, "Finalizing upload…");

  const finalForm = new FormData();
  // Append the server-returned filename under the correct field name for this content type
  finalForm.append(contentFieldName, filePath);

  Object.entries(fields).forEach(([k, v]) => {
    if (k === "directory") return;       // file already stored; not needed in finalize
    if (k === contentFieldName) return;  // already set above from filePath — skip to avoid override with file.name
    if (v === undefined || v === null) return;
    if (v instanceof File) finalForm.append(k, v, v.name);
    else if (v !== "") finalForm.append(k, v);
  });

  const finalRes = await apiClient.post(endpoint, finalForm, {
    headers: { "Content-Type": "multipart/form-data" },
  });

  onProgress?.(100, progressLabel);
  return finalRes.data;
}

/* ── Image upload (single chunk via feed_content_upload) ────────────────────── */

export async function uploadImage(
  file: File,
  onProgress?: UploadProgressCallback,
  label = "Uploading image…",
): Promise<string> {
  const form = new FormData();
  form.append("chunk_index", "0");
  form.append("total_chunks", "1");
  form.append("file", file, file.name);
  form.append("content_type", "1");
  form.append("directory", "upload");

  const res = await apiClient.post(API_ENDPOINTS.UPLOAD.FEED_CONTENT, form, {
    headers: { "Content-Type": "multipart/form-data" },
  });
  onProgress?.(100, label);
  return (res.data?.result?.image as string) ?? "";
}

/* ── Service ────────────────────────────────────────────────────────────────── */

export const uploadService = {

  getLanguages: (): Promise<Language[]> =>
    apiClient.post(API_ENDPOINTS.GET_LANGUAGE).then((r) => r.data.result ?? []),

  getPodcasts: (channelId: string): Promise<PodcastItem[]> =>
    apiClient
      .post(API_ENDPOINTS.GET_CONTENT_BY_CHANNEL, { channel_id: channelId, content_type: 4, page_no: 1 })
      .then((r) => (r.data as ContentByChannelResponse).result ?? []),

  /* ── Video ── */
  uploadVideo: async (params: {
    file: File;
    portraitImg?: File;
    landscapeImg?: File;
    title: string; channelId: string; categoryId: string; languageId: string;
    description: string; isComment: string; isDownload: string; isLike: string;
    isRent: string; rentPrice: string; rentDay: string; contentDuration: string;
  }, onProgress?: UploadProgressCallback) => {
    return uploadInChunks(API_ENDPOINTS.UPLOAD.VIDEO, params.file, {
      directory: "videos", title: params.title, channel_id: params.channelId,
      category_id: params.categoryId, language_id: params.languageId,
      portrait_img: params.portraitImg,
      landscape_img: params.landscapeImg,
      description: params.description,
      is_comment: params.isComment, is_download: params.isDownload,
      is_like: params.isLike, is_rent: params.isRent,
      rent_price: params.rentPrice, rent_day: params.rentDay,
      content_duration: params.contentDuration,
    }, onProgress, "Uploading video…", "video");
  },

  /* ── Music ── */
  uploadMusic: async (params: {

    file: File; portraitImg?: File; landscapeImg?: File;
    title: string; channelId: string; categoryId: string; languageId: string;
    description: string; isComment: string; isDownload: string; isLike: string;
    contentDuration: string;
  }, onProgress?: UploadProgressCallback) => {
    return uploadInChunks(API_ENDPOINTS.UPLOAD.MUSIC, params.file, {
      directory: "musics", title: params.title, channel_id: params.channelId,
      category_id: params.categoryId, language_id: params.languageId,
      portrait_img: params.portraitImg,
      landscape_img: params.landscapeImg,

      description: params.description, is_comment: params.isComment,
      is_download: params.isDownload, is_like: params.isLike,
      content_duration: params.contentDuration,
    }, onProgress, "Uploading music…", "music");
  },

  /* ── Reels ── */
  uploadReels: async (params: {
    file: File; portraitImg?: File;
    channelId: string; title: string;
    isComment: string; isDownload: string; isLike: string;
  }, onProgress?: UploadProgressCallback) => {


    return uploadInChunks(API_ENDPOINTS.UPLOAD.REELS, params.file, {
      directory: "Reels", original_file_name: params.file.name, channel_id: params.channelId,
      title: params.title, portrait_img: params.portraitImg,
      is_comment: params.isComment, is_download: params.isDownload, is_like: params.isLike,
    }, onProgress, "Uploading reel…", "video");
  },

  /* ── Radio ── */
  uploadRadio: async (params: {
    file: File; portraitImg?: File; landscapeImg?: File;
    title: string; channelId: string; description: string;
    isComment: string; isLike: string;
  }, onProgress?: UploadProgressCallback) => {



    return uploadInChunks(API_ENDPOINTS.UPLOAD.RADIO, params.file, {
      directory: "radios", title: params.title, channel_id: params.channelId,
      portrait_img: params.portraitImg,
      landscape_img: params.landscapeImg,
      is_comment: params.isComment, is_like: params.isLike, description: params.description,
    }, onProgress, "Uploading radio…", "radio");
  },

  /* ── Episode ── */
  uploadEpisode: async (params: {
    file: File; portraitImg?: File; landscapeImg?: File;
    podcastsId: string; name: string; description: string;
    isComment: string; isDownload: string; isLike: string;
  }, onProgress?: UploadProgressCallback) => {


    return uploadInChunks(API_ENDPOINTS.UPLOAD.EPISODE, params.file, {
      directory: "episodes", podcasts_id: params.podcastsId, name: params.name,
      portrait_img: params.portraitImg,
      landscape_img: params.landscapeImg,
      is_comment: params.isComment, is_download: params.isDownload,
      is_like: params.isLike, description: params.description,
    }, onProgress, "Uploading episode…", "episode");
  },

  /* ── Feed ── */
  uploadFeedContent: async (
    file: File, contentType: "1" | "2", onProgress?: UploadProgressCallback,
  ): Promise<FeedContentResult> => {

    /* ── Image: single direct upload ───────────────────────────────────────
       Send the binary file as both "file" and "image" in one request.
    ────────────────────────────────────────────────────────────────────── */
    if (contentType === "1") {
      const form = new FormData();
      form.append("chunk_index", "0");
      form.append("total_chunks", "1");
      form.append("file", file, file.name);
      form.append("content_type", "1");
      form.append("directory", "feed");
      form.append("image", file, file.name);

      const res = await apiClient.post(API_ENDPOINTS.UPLOAD.FEED_CONTENT, form, {
        headers: { "Content-Type": "multipart/form-data" },
      });
      onProgress?.(100, "Image uploaded");
      return (res.data?.result ?? {}) as FeedContentResult;
    }

    /* ── Video: chunked binary upload → finalize with server filename ───────
       Phase 1: upload file in chunks (just binary + directory)
       Phase 2: uploadInChunks sends  video: "<server_filename>"  in the
                final metadata call so the server links it to the feed post.
    ────────────────────────────────────────────────────────────────────── */
    const raw = await uploadInChunks(
      API_ENDPOINTS.UPLOAD.FEED_CONTENT,
      file,
      { directory: "feed", content_type: "2" },
      onProgress,
      "Uploading video…",
      "video",   // Phase 2 sends:  video: "<vid_...mp4>"
    );
    // The feed content upload returns the result directly or nested
    const d = raw as Record<string, unknown>;
    return (d?.result ?? d ?? {}) as FeedContentResult;
  },

  uploadFeed: (params: {
    channelId: string; description: string; isLike: string; isComment: string;
    postContent: { content_type: string; image: string; video: string }[];
  }) =>
    apiClient.post(API_ENDPOINTS.UPLOAD.FEED, {
      channel_id: params.channelId,
      description: params.description,
      is_like: params.isLike,
      is_comment: params.isComment,
      post_content: JSON.stringify(params.postContent),
    }).then((r) => r.data),

  /* ── Podcast (series, no main file — images only) ── */
  createPodcast: async (
    params: {
      channelId: string;
      categoryId: string;
      languageId: string;
      title: string;
      description: string;
      portraitImg?: File;
      landscapeImg?: File;
    },
    onProgress?: UploadProgressCallback
  ) => {

    onProgress?.(90, "Creating podcast…");

    const form = new FormData();

    form.append("channel_id", params.channelId);
    form.append("category_id", params.categoryId);
    form.append("language_id", params.languageId);

    form.append("title", params.title);
    form.append("description", params.description);

    if (params.portraitImg) {
      form.append("portrait_img", params.portraitImg);
    }

    if (params.landscapeImg) {
      form.append("landscape_img", params.landscapeImg);
    }
    const res = await apiClient.post(
      API_ENDPOINTS.UPLOAD.PODCAST,
      form,
      {
        headers: {
          "Content-Type": "multipart/form-data",
        },
      }
    );

    onProgress?.(100, "Done!");

    return res.data;
  },
};
