import { fetch as tauriFetch } from "@tauri-apps/plugin-http";

/**
 * 跨空间感知同步模块
 *
 * 使用 GitHub Contents API 读写 .dev-spaces/spaces.json
 * 记录每个空间各项目的最新 push 信息，实现多设备互相感知
 *
 * spaces.json 结构：
 * {
 *   "version": 1,
 *   "spaces": {
 *     "<space_id>": {
 *       "name": "会旅游的笔记本",
 *       "projects": {
 *         "<repo_url>": {
 *           "branch": "feature/login",
 *           "last_push_at": "2026-03-31T10:00:00Z",
 *           "last_commit_msg": "fix: login bug"
 *         }
 *       }
 *     }
 *   }
 * }
 */

const DEV_SPACES_REPO = ".dev-spaces";
const SPACES_FILE = "spaces.json";

export interface SpaceProjectInfo {
  branch: string;
  last_push_at: string;
  last_commit_msg?: string;
}

/** 项目档案条目：记录某个空间内某个项目的元信息 */
export interface SpaceCatalogItem {
  id: string;             // 在该空间内的 workspace_id
  name: string;
  description?: string;
  tech_stack?: string;
  repo_url: string;
  default_branch?: string;
  platform?: string;
}

export interface SpaceInfo {
  name: string;
  projects: Record<string, SpaceProjectInfo>; // key = repo_url，记录 push 事件
  catalog?: SpaceCatalogItem[];               // 项目档案列表，用于跨空间发现
}

export interface SpacesJson {
  version: number;
  spaces: Record<string, SpaceInfo>; // key = space_id
}

interface GithubFileResponse {
  content: string;
  sha: string;
  encoding: string;
}

// ── GitHub Contents API ─────────────────────────────────────

async function apiHeaders(token: string) {
  return {
    Authorization: `Bearer ${token}`,
    "User-Agent": "dev-manager-tauri",
    Accept: "application/vnd.github+json",
    "Content-Type": "application/json",
  };
}

/** 确保 .dev-spaces 仓库存在（不存在则创建） */
export async function ensureDevSpacesRepo(token: string, username: string): Promise<void> {
  const headers = await apiHeaders(token);

  // 检查是否已存在
  const check = await tauriFetch(
    `https://api.github.com/repos/${username}/${DEV_SPACES_REPO}`,
    { headers }
  );
  if (check.ok) return;

  // 创建私有仓库
  const create = await tauriFetch("https://api.github.com/user/repos", {
    method: "POST",
    headers,
    body: JSON.stringify({
      name: DEV_SPACES_REPO,
      private: true,
      description: "Dev Space Manager - 跨设备开发空间配置",
      auto_init: true,
    }),
  });
  if (!create.ok) {
    const err = await create.json().catch(() => ({}));
    // 422 通常是仓库已存在，忽略
    if (create.status !== 422) {
      throw new Error(`创建 .dev-spaces 仓库失败: ${JSON.stringify(err)}`);
    }
  }
}

/** 读取 spaces.json，返回内容和 sha（用于后续更新） */
export async function readSpacesJson(
  token: string,
  username: string
): Promise<{ data: SpacesJson; sha: string } | null> {
  const headers = await apiHeaders(token);
  const resp = await tauriFetch(
    `https://api.github.com/repos/${username}/${DEV_SPACES_REPO}/contents/${SPACES_FILE}`,
    { headers }
  );
  if (resp.status === 404) return null;
  if (!resp.ok) throw new Error(`读取 spaces.json 失败: ${resp.status}`);

  const file: GithubFileResponse = await resp.json();
  const content = atob(file.content.replace(/\n/g, ""));
  const data: SpacesJson = JSON.parse(content);
  return { data, sha: file.sha };
}

/** 将 SpacesJson 编码为 base64 content */
function encodeSpacesJson(data: SpacesJson): string {
  const json = JSON.stringify(data, null, 2);
  const bytes = new TextEncoder().encode(json);
  // 分块处理，防止大文件时 String.fromCharCode(...bytes) 超出调用栈
  let binary = "";
  const chunkSize = 8192;
  for (let i = 0; i < bytes.length; i += chunkSize) {
    binary += String.fromCharCode(...bytes.subarray(i, i + chunkSize));
  }
  return btoa(binary);
}

/** 写入 spaces.json（create 或 update），409 冲突时自动重新获取 sha 重试一次 */
export async function writeSpacesJson(
  token: string,
  username: string,
  data: SpacesJson,
  sha?: string
): Promise<void> {
  const headers = await apiHeaders(token);
  const content = encodeSpacesJson(data);

  const doWrite = async (currentSha?: string) => {
    const body: Record<string, string> = {
      message: "chore: update dev spaces config",
      content,
    };
    if (currentSha) body.sha = currentSha;
    return tauriFetch(
      `https://api.github.com/repos/${username}/${DEV_SPACES_REPO}/contents/${SPACES_FILE}`,
      { method: "PUT", headers, body: JSON.stringify(body) }
    );
  };

  let resp = await doWrite(sha);

  // 409 说明远端 sha 已变，重新读取最新 sha 再重试一次
  if (resp.status === 409) {
    const latest = await readSpacesJson(token, username);
    resp = await doWrite(latest?.sha);
  }

  if (!resp.ok) {
    const err = await resp.json().catch(() => ({}));
    throw new Error(`写入 spaces.json 失败: ${JSON.stringify(err)}`);
  }
}

// ── 高层操作 ────────────────────────────────────────────────

/**
 * 启动时同步：拉取远端 spaces.json
 * 返回所有空间的项目信息（用于跨空间感知展示）
 */
export async function fetchRemoteSpaces(
  token: string,
  username: string
): Promise<SpacesJson | null> {
  try {
    await ensureDevSpacesRepo(token, username);
    const result = await readSpacesJson(token, username);
    return result?.data ?? null;
  } catch (e) {
    console.warn("跨空间同步失败（不影响本地功能）:", e);
    return null;
  }
}

/**
 * Push 后更新：把当前空间的项目 push 信息写回远端
 */
export async function recordPushToRemote(
  token: string,
  username: string,
  spaceId: string,
  spaceName: string,
  repoUrl: string,
  branch: string,
  lastCommitMsg?: string
): Promise<void> {
  await ensureDevSpacesRepo(token, username);
  const existing = await readSpacesJson(token, username);

  const now = new Date().toISOString();
  const spacesData: SpacesJson = existing?.data ?? { version: 1, spaces: {} };

  if (!spacesData.spaces[spaceId]) {
    spacesData.spaces[spaceId] = { name: spaceName, projects: {} };
  }
  spacesData.spaces[spaceId].name = spaceName;
  spacesData.spaces[spaceId].projects[repoUrl] = {
    branch,
    last_push_at: now,
    last_commit_msg: lastCommitMsg,
  };

  await writeSpacesJson(token, username, spacesData, existing?.sha);
}

/**
 * 将当前空间的某个项目同步到 .dev-spaces catalog
 * 在项目创建/更新后调用，让其他空间可以发现此项目
 */
export async function syncProjectCatalog(
  token: string,
  username: string,
  spaceId: string,
  spaceName: string,
  project: {
    id: string;
    name: string;
    description?: string;
    tech_stack?: string;
    repo_url: string;
    default_branch?: string;
    platform?: string;
  }
): Promise<void> {
  await ensureDevSpacesRepo(token, username);
  const existing = await readSpacesJson(token, username);
  const spacesData: SpacesJson = existing?.data ?? { version: 1, spaces: {} };

  if (!spacesData.spaces[spaceId]) {
    spacesData.spaces[spaceId] = { name: spaceName, projects: {} };
  }
  spacesData.spaces[spaceId].name = spaceName;

  const catalog = spacesData.spaces[spaceId].catalog ?? [];
  const idx = catalog.findIndex((c) => c.repo_url === project.repo_url);
  const item: SpaceCatalogItem = {
    id: project.id,
    name: project.name,
    description: project.description,
    tech_stack: project.tech_stack,
    repo_url: project.repo_url,
    default_branch: project.default_branch,
    platform: project.platform,
  };
  if (idx >= 0) catalog[idx] = item;
  else catalog.push(item);
  spacesData.spaces[spaceId].catalog = catalog;

  await writeSpacesJson(token, username, spacesData, existing?.sha);
}

/**
 * 从 SpacesJson 中提取某个项目在其他空间的信息
 */
export function getOtherSpacesInfo(
  spacesJson: SpacesJson,
  currentSpaceId: string,
  repoUrl: string
): Array<{ spaceId: string; spaceName: string } & SpaceProjectInfo> {
  const result: Array<{ spaceId: string; spaceName: string } & SpaceProjectInfo> = [];
  for (const [spaceId, spaceInfo] of Object.entries(spacesJson.spaces)) {
    if (spaceId === currentSpaceId) continue;
    const proj = spaceInfo.projects[repoUrl];
    if (proj) {
      result.push({ spaceId, spaceName: spaceInfo.name, ...proj });
    }
  }
  // 按时间倒序
  result.sort((a, b) => b.last_push_at.localeCompare(a.last_push_at));
  return result;
}

/**
 * 找出其他空间有、但当前空间没有（按 repo_url 判断）的项目
 */
export function getDiscoverableProjects(
  spacesJson: SpacesJson,
  currentSpaceId: string,
  currentRepoUrls: Set<string>
): Array<SpaceCatalogItem & { spaceId: string; spaceName: string }> {
  const result: Array<SpaceCatalogItem & { spaceId: string; spaceName: string }> = [];
  // 用 repo_url 去重（同一仓库可能出现在多个其他空间）
  const seen = new Set<string>();
  for (const [spaceId, spaceInfo] of Object.entries(spacesJson.spaces)) {
    if (spaceId === currentSpaceId) continue;
    for (const item of (spaceInfo.catalog ?? [])) {
      if (!currentRepoUrls.has(item.repo_url) && !seen.has(item.repo_url)) {
        seen.add(item.repo_url);
        result.push({ ...item, spaceId, spaceName: spaceInfo.name });
      }
    }
  }
  return result;
}

/** 格式化相对时间（如 "3小时前"） */
export function formatRelativeTime(isoString: string): string {
  const diff = Date.now() - new Date(isoString).getTime();
  const minutes = Math.floor(diff / 60000);
  if (minutes < 1) return "刚刚";
  if (minutes < 60) return `${minutes}分钟前`;
  const hours = Math.floor(minutes / 60);
  if (hours < 24) return `${hours}小时前`;
  const days = Math.floor(hours / 24);
  if (days < 30) return `${days}天前`;
  return new Date(isoString).toLocaleDateString("zh-CN");
}
