import fs from "fs";
import path from "path";
import crypto from "crypto";
import { Metadata, Category, DocFile } from "./types";

const UPLOADS_DIR = process.env.UPLOADS_DIR ?? path.join(process.cwd(), "uploads");
const DATA_FILE   = path.join(UPLOADS_DIR, ".metadata.json");
const THUMBS_DIR  = path.join(UPLOADS_DIR, ".thumbs");
const TMP_FILE    = DATA_FILE + ".tmp";

export function ensureUploadsDir() {
  if (!fs.existsSync(UPLOADS_DIR)) fs.mkdirSync(UPLOADS_DIR, { recursive: true });
  if (!fs.existsSync(THUMBS_DIR))  fs.mkdirSync(THUMBS_DIR, { recursive: true });
}

export const DEFAULT_CATEGORIES: Category[] = [
  { id: "personal",  name: "Persönlich", color: "#ff8091", icon: "user"      },
  { id: "invoices",  name: "Rechnungen", color: "#FF9F0A", icon: "banknote"  },
  { id: "health",    name: "Gesundheit", color: "#BF5AF2", icon: "heart"     },
  { id: "finance",   name: "Finanzen",   color: "#30D158", icon: "banknote"  },
  { id: "work",      name: "Arbeit",     color: "#0A84FF", icon: "briefcase" },
  { id: "sources",   name: "Quellen",    color: "#5AC8FA", icon: "book-open" },
  { id: "other",     name: "Sonstiges",  color: "#8E8E93", icon: "folder"    },
];

const VALID_IDS = new Set(DEFAULT_CATEGORIES.map(c => c.id));

export function readMetadata(): Metadata {
  ensureUploadsDir();
  if (!fs.existsSync(DATA_FILE)) {
    const defaultData: Metadata = { files: {}, categories: DEFAULT_CATEGORIES };
    writeMetadata(defaultData);
    return defaultData;
  }
  const data = JSON.parse(fs.readFileSync(DATA_FILE, "utf-8"));
  data.categories = DEFAULT_CATEGORIES;
  for (const file of Object.values(data.files) as DocFile[]) {
    if (file.categoryId && !VALID_IDS.has(file.categoryId)) file.categoryId = null;
    if (!Array.isArray(file.tags)) file.tags = [];
  }
  return data;
}

export function writeMetadata(data: Metadata) {
  ensureUploadsDir();
  data.categories = DEFAULT_CATEGORIES;
  // Atomic write: write to temp, then rename
  fs.writeFileSync(TMP_FILE, JSON.stringify(data, null, 2));
  fs.renameSync(TMP_FILE, DATA_FILE);
}

export function getUploadsDir() {
  return UPLOADS_DIR;
}

export function getThumbsDir() {
  return THUMBS_DIR;
}

// --- security helpers ---

const ALLOWED_MIME = new Set([
  "application/pdf",
  "image/jpeg",
  "image/png",
  "image/webp",
  "image/gif",
  "image/heic",
  "image/heif",
  "text/plain",
  "text/markdown",
  "text/csv",
  "application/msword",
  "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
  "application/vnd.ms-excel",
  "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
  "application/vnd.ms-powerpoint",
  "application/vnd.openxmlformats-officedocument.presentationml.presentation",
  "application/zip",
  "application/x-zip-compressed",
]);

export function isAllowedMime(type: string): boolean {
  return ALLOWED_MIME.has(type);
}

// MIME types that may be served inline. Everything else is forced to attachment
// to prevent stored-XSS via SVG / HTML / JS uploads.
const SAFE_INLINE_MIME = new Set([
  "application/pdf",
  "image/jpeg",
  "image/png",
  "image/webp",
  "image/gif",
  "text/plain",
]);

export function isInlineSafe(type: string): boolean {
  return SAFE_INLINE_MIME.has(type);
}

// Strip path separators, traversal sequences, and control chars.
// Always returns a non-empty filename, falling back to a hash if needed.
export function sanitizeFilename(name: string, fallbackExt = ""): string {
  const just = path.basename(name);                 // strip directory parts
  const cleaned = just
    .replace(/[\x00-\x1f\x7f]/g, "")                // control chars
    .replace(/[/\\]/g, "")                          // any remaining slashes
    .replace(/^\.+/, "")                            // leading dots (e.g. .htaccess)
    .replace(/[<>:"|?*]/g, "_")                     // win/illegal chars
    .trim();
  if (cleaned && cleaned !== "." && cleaned !== "..") return cleaned;
  return crypto.randomBytes(6).toString("hex") + fallbackExt;
}

export function generateId(): string {
  return "doc_" + crypto.randomBytes(8).toString("hex");
}

// Resolve a stored-file path safely under uploadsDir.
// Returns null if the result would escape the directory.
export function safeUploadPath(storedName: string): string | null {
  const target = path.resolve(UPLOADS_DIR, storedName);
  const root   = path.resolve(UPLOADS_DIR) + path.sep;
  if (!target.startsWith(root) && target + path.sep !== root) return null;
  return target;
}

export function thumbPathFor(id: string): string {
  return path.join(THUMBS_DIR, id + ".webp");
}
