import { FastifyInstance } from "fastify";
import prisma from "../../config/database";
import {
  requireAuth,
  requirePermission,
  requireAnyPermission,
  optionalAuth,
} from "../../middleware/auth";
import { logAudit } from "../../services/audit";
import { success, error } from "../../utils/response";
import multipart from "@fastify/multipart";
import { parseBody } from "../../schemas/common";
import { UpdateCompanySettingsSchema } from "../../schemas/settings.schema";
import { invalidateSettingsCache } from "../../services/system-settings";
import os from "os";
import { config } from "../../config/env";
import { NasFileManager } from "../../services/nas-file-manager";
import {
  nasInvoicesManager,
  nasOrdersManager,
} from "../../services/nas-financials-manager";
import { nasOffersManager } from "../../services/nas-offers-manager";

/** Encode custom_fields + supplier_field_order into a single JSON blob (matching PHP format) */
function encodeCustomFields(
  fields: unknown,
  fieldOrder: unknown,
): string | null {
  const f = Array.isArray(fields) ? fields : [];
  const o = Array.isArray(fieldOrder) ? fieldOrder : [];
  if (f.length === 0 && o.length === 0) return null;
  return JSON.stringify({ fields: f, field_order: o });
}

/** Decode custom_fields JSON blob into separate fields + field_order for frontend */
function decodeCustomFields(raw: string | null): {
  custom_fields: unknown[];
  supplier_field_order: string[];
} {
  if (!raw) return { custom_fields: [], supplier_field_order: [] };
  try {
    const parsed = JSON.parse(raw);
    // PHP format: { fields: [...], field_order: [...] }
    if (
      parsed &&
      typeof parsed === "object" &&
      !Array.isArray(parsed) &&
      "fields" in parsed
    ) {
      return {
        custom_fields: parsed.fields || [],
        supplier_field_order: parsed.field_order || [],
      };
    }
    // Legacy TS format: raw array
    if (Array.isArray(parsed)) {
      return { custom_fields: parsed, supplier_field_order: [] };
    }
    return { custom_fields: [], supplier_field_order: [] };
  } catch {
    return { custom_fields: [], supplier_field_order: [] };
  }
}

export default async function companySettingsRoutes(
  fastify: FastifyInstance,
): Promise<void> {
  await fastify.register(multipart, { limits: { fileSize: 5 * 1024 * 1024 } });

  // GET /api/admin/company-settings/logo?variant=light|dark
  fastify.get("/logo", { preHandler: optionalAuth }, async (request, reply) => {
    const query = request.query as Record<string, string>;
    const variant = query.variant === "dark" ? "dark" : "light";
    const column = variant === "dark" ? "logo_data_dark" : "logo_data";

    const settings = await prisma.company_settings.findFirst({
      select: { [column]: true },
    });
    const buf = settings?.[column] as unknown as Buffer | null;
    if (!buf) return error(reply, "Logo nenalezeno", 404);

    let mime = "image/png";
    if (buf[0] === 0xff && buf[1] === 0xd8) mime = "image/jpeg";
    else if (buf[0] === 0x47 && buf[1] === 0x49) mime = "image/gif";
    else if (
      buf[0] === 0x52 &&
      buf[1] === 0x49 &&
      buf[2] === 0x46 &&
      buf[3] === 0x46 &&
      buf[8] === 0x57 &&
      buf[9] === 0x45 &&
      buf[10] === 0x42 &&
      buf[11] === 0x50
    )
      mime = "image/webp";

    return reply
      .type(mime)
      .header("Cache-Control", "public, max-age=3600")
      .send(buf);
  });

  // POST /api/admin/company-settings/logo?variant=light|dark
  fastify.post(
    "/logo",
    { preHandler: requirePermission("settings.company") },
    async (request, reply) => {
      const query = request.query as Record<string, string>;
      const variant = query.variant === "dark" ? "dark" : "light";
      const column = variant === "dark" ? "logo_data_dark" : "logo_data";

      const file = await request.file();
      if (!file) return error(reply, "Nebyl nahrán žádný soubor", 400);

      const allowed = ["image/png", "image/jpeg", "image/gif", "image/webp"];
      if (!allowed.includes(file.mimetype)) {
        return error(
          reply,
          "Nepodporovaný formát. Povoleno: PNG, JPG, GIF, WebP",
          400,
        );
      }

      const buffer = await file.toBuffer();
      const existing = await prisma.company_settings.findFirst();
      if (!existing) return error(reply, "Nastavení nenalezeno", 404);

      await prisma.company_settings.update({
        where: { id: existing.id },
        data: { [column]: new Uint8Array(buffer), modified_at: new Date() },
      });

      await logAudit({
        request,
        authData: request.authData,
        action: "update",
        entityType: "company_settings",
        entityId: existing.id,
        description: `Nahráno logo (${variant})`,
      });
      return success(reply, null, 200, "Logo nahráno");
    },
  );

  fastify.get("/", { preHandler: requireAuth }, async (_request, reply) => {
    // Use upsert to avoid race condition between findFirst and create
    let settings = await prisma.company_settings.findFirst({
      select: {
        id: true,
        company_name: true,
        street: true,
        city: true,
        postal_code: true,
        country: true,
        company_id: true,
        vat_id: true,
        custom_fields: true,
        quotation_prefix: true,
        default_currency: true,
        default_vat_rate: true,
        uuid: true,
        modified_at: true,
        is_deleted: true,
        sync_version: true,
        order_type_code: true,
        invoice_type_code: true,
        require_2fa: true,
        break_threshold_hours: true,
        break_duration_short: true,
        break_duration_long: true,
        clock_rounding_minutes: true,
        invoice_alert_email: true,
        leave_notify_email: true,
        max_login_attempts: true,
        lockout_minutes: true,
        max_requests_per_minute: true,
        available_vat_rates: true,
        available_currencies: true,
        smtp_from: true,
        smtp_from_name: true,
        offer_number_pattern: true,
        order_number_pattern: true,
        invoice_number_pattern: true,
        issued_order_number_pattern: true,
        issued_order_type_code: true,
        project_number_pattern: true,
        project_type_code: true,
        warehouse_receipt_prefix: true,
        warehouse_receipt_number_pattern: true,
        warehouse_issue_prefix: true,
        warehouse_issue_number_pattern: true,
        warehouse_inventory_prefix: true,
        warehouse_inventory_number_pattern: true,
      },
    });

    if (!settings) {
      // Wrap create in a transaction to handle race condition:
      // another request may have created settings between findFirst and create
      settings = await prisma.$transaction(async (tx) => {
        // Double-check inside transaction
        const existing = await tx.company_settings.findFirst({
          select: { id: true },
        });
        if (existing) {
          return tx.company_settings.findUnique({
            where: { id: existing.id },
            select: {
              id: true,
              company_name: true,
              street: true,
              city: true,
              postal_code: true,
              country: true,
              company_id: true,
              vat_id: true,
              custom_fields: true,
              quotation_prefix: true,
              default_currency: true,
              default_vat_rate: true,
              uuid: true,
              modified_at: true,
              is_deleted: true,
              sync_version: true,
              order_type_code: true,
              invoice_type_code: true,
              require_2fa: true,
              break_threshold_hours: true,
              break_duration_short: true,
              break_duration_long: true,
              clock_rounding_minutes: true,
              invoice_alert_email: true,
              leave_notify_email: true,
              max_login_attempts: true,
              lockout_minutes: true,
              max_requests_per_minute: true,
              available_vat_rates: true,
              available_currencies: true,
              smtp_from: true,
              smtp_from_name: true,
              offer_number_pattern: true,
              order_number_pattern: true,
              invoice_number_pattern: true,
              issued_order_number_pattern: true,
              issued_order_type_code: true,
              project_number_pattern: true,
              project_type_code: true,
              warehouse_receipt_prefix: true,
              warehouse_receipt_number_pattern: true,
              warehouse_issue_prefix: true,
              warehouse_issue_number_pattern: true,
              warehouse_inventory_prefix: true,
              warehouse_inventory_number_pattern: true,
            },
          });
        }
        return tx.company_settings.create({
          data: {
            company_name: "",
            quotation_prefix: "N",
            default_currency: "EUR",
            default_vat_rate: 21.0,
          },
          select: {
            id: true,
            company_name: true,
            street: true,
            city: true,
            postal_code: true,
            country: true,
            company_id: true,
            vat_id: true,
            custom_fields: true,
            quotation_prefix: true,
            default_currency: true,
            default_vat_rate: true,
            uuid: true,
            modified_at: true,
            is_deleted: true,
            sync_version: true,
            order_type_code: true,
            invoice_type_code: true,
            require_2fa: true,
            break_threshold_hours: true,
            break_duration_short: true,
            break_duration_long: true,
            clock_rounding_minutes: true,
            invoice_alert_email: true,
            leave_notify_email: true,
            max_login_attempts: true,
            lockout_minutes: true,
            max_requests_per_minute: true,
            available_vat_rates: true,
            available_currencies: true,
            smtp_from: true,
            smtp_from_name: true,
            offer_number_pattern: true,
            order_number_pattern: true,
            invoice_number_pattern: true,
            issued_order_number_pattern: true,
            issued_order_type_code: true,
            project_number_pattern: true,
            project_type_code: true,
            warehouse_receipt_prefix: true,
            warehouse_receipt_number_pattern: true,
            warehouse_issue_prefix: true,
            warehouse_issue_number_pattern: true,
            warehouse_inventory_prefix: true,
            warehouse_inventory_number_pattern: true,
          },
        });
      });
    }
    if (!settings) return error(reply, "Nastavení nenalezeno", 500);

    // Check if logo exists
    const logoCheck = await prisma.company_settings.findFirst({
      where: { id: settings.id },
      select: { logo_data: true, logo_data_dark: true },
    });
    const has_logo = !!logoCheck?.logo_data;
    const has_logo_dark = !!logoCheck?.logo_data_dark;

    const { custom_fields, supplier_field_order } = decodeCustomFields(
      settings.custom_fields as string | null,
    );

    const pkg = require("../../../package.json") as { version: string };

    let available_vat_rates: number[] = [0, 10, 12, 15, 21];
    try {
      const raw = settings.available_vat_rates as string | null;
      if (raw) {
        const parsed = JSON.parse(raw);
        if (Array.isArray(parsed) && parsed.length > 0)
          available_vat_rates = parsed;
      }
    } catch {
      /* ignore */
    }

    let available_currencies: string[] = ["CZK", "EUR", "USD", "GBP"];
    try {
      const raw = settings.available_currencies as string | null;
      if (raw) {
        const parsed = JSON.parse(raw);
        if (Array.isArray(parsed) && parsed.length > 0)
          available_currencies = parsed;
      }
    } catch {
      /* ignore */
    }

    return success(reply, {
      ...settings,
      custom_fields,
      supplier_field_order,
      available_vat_rates,
      available_currencies,
      has_logo,
      has_logo_dark,
      app_version: pkg.version,
    });
  });

  // GET /api/admin/company-settings/system-info
  fastify.get(
    "/system-info",
    { preHandler: requirePermission("settings.system") },
    async (request, reply) => {
      const pkg = require("../../../package.json") as { version: string };
      const uptimeSec = process.uptime();
      const days = Math.floor(uptimeSec / 86400);
      const hours = Math.floor((uptimeSec % 86400) / 3600);
      const mins = Math.floor((uptimeSec % 3600) / 60);
      const uptimeStr =
        days > 0
          ? `${days}d ${hours}h ${mins}m`
          : hours > 0
            ? `${hours}h ${mins}m`
            : `${mins}m`;

      const mem = process.memoryUsage();
      const totalMem = os.totalmem();
      const freeMem = os.freemem();

      // DB connection check
      let dbStatus = "ok";
      let migrationCount = 0;
      try {
        const result = await prisma.$queryRaw<[{ cnt: bigint }]>`
          SELECT COUNT(*) as cnt FROM _prisma_migrations WHERE finished_at IS NOT NULL
        `;
        migrationCount = Number(result[0]?.cnt ?? 0);
      } catch (err) {
        dbStatus = "error";
        request.log.error(err, "DB health check failed");
      }

      // NAS status
      const projectNas = new NasFileManager();

      return success(reply, {
        app_version: pkg.version,
        node_version: process.version,
        platform: `${os.type()} ${os.release()}`,
        uptime: uptimeStr,
        environment: config.appEnv,
        timezone:
          process.env.TZ || Intl.DateTimeFormat().resolvedOptions().timeZone,
        memory: {
          rss: `${Math.round(mem.rss / 1024 / 1024)} MB`,
          heap_used: `${Math.round(mem.heapUsed / 1024 / 1024)} MB`,
          heap_total: `${Math.round(mem.heapTotal / 1024 / 1024)} MB`,
          system_total: `${Math.round(totalMem / 1024 / 1024)} MB`,
          system_free: `${Math.round(freeMem / 1024 / 1024)} MB`,
        },
        database: {
          status: dbStatus,
          migrations_applied: migrationCount,
        },
        nas: {
          projects: {
            configured: projectNas.isConfigured(),
          },
          invoices: {
            configured: nasInvoicesManager.isConfigured(),
          },
          orders: {
            configured: nasOrdersManager.isConfigured(),
          },
          offers: {
            configured: nasOffersManager.isConfigured(),
          },
        },
      });
    },
  );

  fastify.put(
    "/",
    { preHandler: requireAnyPermission("settings.company", "settings.system") },
    async (request, reply) => {
      const parsed = parseBody(UpdateCompanySettingsSchema, request.body);
      if ("error" in parsed) return error(reply, parsed.error, 400);
      const body = parsed.data;

      const existing = await prisma.company_settings.findFirst();
      if (!existing) return error(reply, "Nastavení nenalezeno", 404);

      const data: Record<string, unknown> = { modified_at: new Date() };
      const strFields = [
        "company_name",
        "street",
        "city",
        "postal_code",
        "country",
        "company_id",
        "vat_id",
        "quotation_prefix",
        "default_currency",
        "order_type_code",
        "invoice_type_code",
        "invoice_alert_email",
        "leave_notify_email",
        "smtp_from",
        "smtp_from_name",
        "offer_number_pattern",
        "order_number_pattern",
        "invoice_number_pattern",
        "issued_order_number_pattern",
        "issued_order_type_code",
        "project_number_pattern",
        "project_type_code",
        "warehouse_receipt_prefix",
        "warehouse_receipt_number_pattern",
        "warehouse_issue_prefix",
        "warehouse_issue_number_pattern",
        "warehouse_inventory_prefix",
        "warehouse_inventory_number_pattern",
      ];
      const bodyRec = body as Record<string, unknown>;
      for (const f of strFields) {
        if (bodyRec[f] !== undefined)
          data[f] = bodyRec[f] ? String(bodyRec[f]) : null;
      }
      if (body.default_vat_rate !== undefined) {
        const vat = Number(body.default_vat_rate);
        if (!Number.isFinite(vat))
          return error(reply, "Neplatná sazba DPH", 400);
        data.default_vat_rate = vat;
      }
      if (body.require_2fa !== undefined) data.require_2fa = !!body.require_2fa;

      const numFields = [
        "break_threshold_hours",
        "break_duration_short",
        "break_duration_long",
        "clock_rounding_minutes",
        "max_login_attempts",
        "lockout_minutes",
        "max_requests_per_minute",
      ] as const;
      for (const f of numFields) {
        if (bodyRec[f] !== undefined) {
          const n = Number(bodyRec[f]);
          if (!Number.isFinite(n))
            return error(reply, `Neplatná hodnota nastavení: ${f}`, 400);
          data[f] = n;
        }
      }

      if (body.available_vat_rates !== undefined)
        data.available_vat_rates = JSON.stringify(body.available_vat_rates);
      if (body.available_currencies !== undefined)
        data.available_currencies = JSON.stringify(body.available_currencies);
      if (
        body.custom_fields !== undefined ||
        body.supplier_field_order !== undefined
      ) {
        let existingFields: unknown[] = [];
        let existingOrder: unknown[] = [];
        if (existing.custom_fields) {
          try {
            const parsed = JSON.parse(existing.custom_fields);
            existingFields = parsed?.fields || [];
            existingOrder = parsed?.field_order || [];
          } catch {
            /* invalid JSON, use defaults */
          }
        }
        data.custom_fields = encodeCustomFields(
          body.custom_fields !== undefined
            ? body.custom_fields
            : existingFields,
          body.supplier_field_order !== undefined
            ? body.supplier_field_order
            : existingOrder,
        );
      }
      data.sync_version = { increment: 1 };

      await prisma.company_settings.update({
        where: { id: existing.id },
        data,
      });

      invalidateSettingsCache();

      await logAudit({
        request,
        authData: request.authData,
        action: "update",
        entityType: "company_settings",
        entityId: existing.id,
        description: "Upraveno firemní nastavení",
      });
      return success(reply, { id: existing.id }, 200, "Nastavení bylo uloženo");
    },
  );
}
