import { Router, Request, Response, NextFunction } from "express";
import { ApiError } from "../shared/errors";
import { env } from "../config/env";
import { successResponse } from "../shared/middleware/errorMiddleware";
import { createMerchantSessionToken, verifyMerchantSessionToken, type MerchantSessionPayload } from "../shared/services/sessionToken";
import { getMerchantByCode, getMerchantById, toMerchantPayload } from "../shared/store/merchantStore";
import {
  getMerchantUserByEmail,
  toMerchantUserPayload,
  verifyMerchantPassword
} from "../shared/store/merchantUserStore";
import {
  createSettlementBatchEvent,
  getSettlementBatchByIdForMerchant,
  getSettlementBatchReportRows,
  getSettlementFinanceSummary,
  listSettlementBatchAdjustments,
  listSettlementBatchEntries,
  listSettlementBatchEvents,
  listSettlementBatches,
  settlementBatchReportToBankGenericCsv,
  settlementBatchReportToCsv,
  SettlementBatchStatus,
  toSettlementBatchAdjustmentPayload,
  toSettlementBatchEntryPayload,
  toSettlementBatchEventPayload,
  toSettlementBatchPayload
} from "../shared/store/settlementStore";
import { createAuditLog } from "../shared/store/auditLogStore";

const router = Router();

type MerchantRequest = Request & {
  merchantId?: string;
  merchantAuth?: {
    mode: "legacy_token" | "session";
    merchant_id: string;
    user_id?: string;
    role_name?: string;
    session?: MerchantSessionPayload;
  };
};

function extractToken(req: Request) {
  const raw = req.header("authorization") || "";
  if (raw.startsWith("Bearer ")) {
    return raw.slice(7);
  }
  return raw || req.header("x-merchant-token") || "";
}

function parseSettlementBatchStatus(value: string | undefined): SettlementBatchStatus | undefined {
  if (value === "created" || value === "paid" || value === "failed" || value === "cancelled") {
    return value;
  }
  return undefined;
}

function parseSettlementExportFormat(value: string | undefined): "standard" | "bank_generic" | undefined {
  if (!value || value === "standard" || value === "general") {
    return "standard";
  }
  if (value === "bank_generic") {
    return "bank_generic";
  }
  return undefined;
}

async function requireMerchantToken(req: MerchantRequest, _res: Response, next: NextFunction) {
  const token = extractToken(req);
  const merchantId = req.header("x-merchant-id") || "";
  if (!token) {
    return next(new ApiError("UNAUTHORIZED", "Missing merchant bearer token", 401));
  }

  const session = verifyMerchantSessionToken(token, env.MERCHANT_SESSION_SECRET);
  if (session) {
    if (merchantId && merchantId !== session.merchant_id) {
      return next(new ApiError("FORBIDDEN", "merchant session cannot access another merchant", 403));
    }
    const merchant = await getMerchantById(session.merchant_id);
    if (!merchant) {
      return next(new ApiError("UNAUTHORIZED", "Invalid merchant scope", 401));
    }
    req.merchantId = merchant.id;
    req.merchantAuth = {
      mode: "session",
      merchant_id: merchant.id,
      user_id: session.sub,
      role_name: session.role_name,
      session
    };
    return next();
  }

  const allowLegacyToken = env.MERCHANT_AUTH_ALLOW_LEGACY_TOKEN !== "false";
  if (!allowLegacyToken || token !== env.MERCHANT_TOKEN) {
    return next(new ApiError("UNAUTHORIZED", "Invalid merchant token", 401));
  }
  if (!merchantId) {
    return next(new ApiError("UNAUTHORIZED", "Missing merchant scope", 401));
  }
  const merchant = await getMerchantById(merchantId);
  if (!merchant) {
    return next(new ApiError("UNAUTHORIZED", "Invalid merchant scope", 401));
  }
  req.merchantId = merchant.id;
  req.merchantAuth = {
    mode: "legacy_token",
    merchant_id: merchant.id
  };
  return next();
}

router.post("/login", async (req: Request, res: Response, next: NextFunction) => {
  const payload = (req.body || {}) as {
    merchant_id?: string;
    merchant_code?: string;
    username?: string;
    password?: string;
  };
  const loginId = (payload.merchant_id || payload.merchant_code || payload.username || "").trim();
  const password = payload.password || "";
  if (!loginId || !password) {
    return next(new ApiError("BAD_REQUEST", "merchant id/code and password are required", 400));
  }

  const merchantUser = loginId.includes("@") ? await getMerchantUserByEmail(loginId) : null;
  if (merchantUser) {
    if (merchantUser.status !== "active" || !verifyMerchantPassword(merchantUser.password_hash, password)) {
      await createAuditLog({
        actor_type: "merchant",
        actor_id: merchantUser.id,
        action: "merchant.login.failed",
        entity_type: "merchant_session",
        entity_id: merchantUser.merchant_id,
        after_json: {
          reason: merchantUser.status !== "active" ? "inactive_user" : "invalid_credentials",
          auth_mode: "session"
        },
        source_ip: req.ip,
        request_id: req.requestId,
        trace_id: req.traceId
      });
      return next(new ApiError("UNAUTHORIZED", "Invalid merchant credentials", 401));
    }
    const merchant = await getMerchantById(merchantUser.merchant_id);
    if (!merchant) {
      return next(new ApiError("UNAUTHORIZED", "Invalid merchant credentials", 401));
    }
    const token = createMerchantSessionToken(
      {
        typ: "merchant",
        sub: merchantUser.id,
        merchant_id: merchant.id,
        name: merchantUser.name,
        email: merchantUser.email,
        role_name: merchantUser.role_name
      },
      env.MERCHANT_SESSION_SECRET,
      env.MERCHANT_SESSION_TTL_SECONDS
    );
    await createAuditLog({
      actor_type: "merchant",
      actor_id: merchantUser.id,
      action: "merchant.login.success",
      entity_type: "merchant_session",
      entity_id: merchant.id,
      after_json: {
        auth_mode: "session",
        role_name: merchantUser.role_name
      },
      source_ip: req.ip,
      request_id: req.requestId,
      trace_id: req.traceId
    });
    return res.json(
      successResponse(req, {
        token,
        auth_mode: "session",
        merchant: toMerchantPayload(merchant),
        user: toMerchantUserPayload(merchantUser)
      })
    );
  }

  if (env.MERCHANT_DEV_LOGIN_ENABLED === "false" || password !== env.MERCHANT_PORTAL_PASSWORD) {
    await createAuditLog({
      actor_type: "merchant",
      actor_id: loginId || "unknown",
      action: "merchant.login.failed",
      entity_type: "merchant_session",
      entity_id: loginId || "unknown",
      after_json: {
        reason: "invalid_legacy_credentials",
        auth_mode: "legacy_token"
      },
      source_ip: req.ip,
      request_id: req.requestId,
      trace_id: req.traceId
    });
    return next(new ApiError("UNAUTHORIZED", "Invalid merchant credentials", 401));
  }

  const merchant = (await getMerchantById(loginId)) || (await getMerchantByCode(loginId));
  if (!merchant) {
    await createAuditLog({
      actor_type: "merchant",
      actor_id: loginId || "unknown",
      action: "merchant.login.failed",
      entity_type: "merchant_session",
      entity_id: loginId || "unknown",
      after_json: {
        reason: "merchant_not_found",
        auth_mode: "legacy_token"
      },
      source_ip: req.ip,
      request_id: req.requestId,
      trace_id: req.traceId
    });
    return next(new ApiError("UNAUTHORIZED", "Invalid merchant credentials", 401));
  }
  await createAuditLog({
    actor_type: "merchant",
    actor_id: merchant.id,
    action: "merchant.login.success",
    entity_type: "merchant_session",
    entity_id: merchant.id,
    after_json: {
      auth_mode: "legacy_token"
    },
    source_ip: req.ip,
    request_id: req.requestId,
    trace_id: req.traceId
  });
  res.json(
    successResponse(req, {
      token: env.MERCHANT_TOKEN,
      auth_mode: "legacy_token",
      merchant: toMerchantPayload(merchant)
    })
  );
});

router.get("/profile", requireMerchantToken, async (req: MerchantRequest, res: Response, next: NextFunction) => {
  const merchant = await getMerchantById(req.merchantId || "");
  if (!merchant) {
    return next(new ApiError("NOT_FOUND", "merchant not found", 404));
  }
  res.json(
    successResponse(req, {
      merchant: toMerchantPayload(merchant),
      auth_mode: req.merchantAuth?.mode,
      user: req.merchantAuth?.session
        ? {
            id: req.merchantAuth.session.sub,
            name: req.merchantAuth.session.name,
            email: req.merchantAuth.session.email,
            role_name: req.merchantAuth.session.role_name
          }
        : null
    })
  );
});

router.get("/settlement-summary", requireMerchantToken, async (req: MerchantRequest, res: Response) => {
  const summary = await getSettlementFinanceSummary({ merchant_id: req.merchantId });
  res.json(successResponse(req, summary));
});

router.get("/settlement-batches", requireMerchantToken, async (req: MerchantRequest, res: Response, next: NextFunction) => {
  const statusRaw = req.query.status as string | undefined;
  const status = parseSettlementBatchStatus(statusRaw);
  const limitRaw = req.query.limit as string | undefined;
  const limit = limitRaw ? Number(limitRaw) : 100;

  if (statusRaw && !status) {
    return next(new ApiError("BAD_REQUEST", "status must be created|paid|failed|cancelled", 400));
  }
  if (!Number.isFinite(limit) || limit <= 0) {
    return next(new ApiError("BAD_REQUEST", "limit must be a positive number", 400));
  }

  const batches = await listSettlementBatches({
    merchant_id: req.merchantId,
    status,
    limit
  });
  res.json(successResponse(req, batches.map(toSettlementBatchPayload)));
});

router.get("/settlement-batches/:batchId", requireMerchantToken, async (req: MerchantRequest, res: Response, next: NextFunction) => {
  const batch = await getSettlementBatchByIdForMerchant(req.params.batchId, req.merchantId || "");
  if (!batch) {
    return next(new ApiError("NOT_FOUND", "settlement batch not found", 404));
  }
  const entries = await listSettlementBatchEntries(batch.id);
  const adjustments = await listSettlementBatchAdjustments(batch.id);
  const events = await listSettlementBatchEvents(batch.id);
  res.json(
    successResponse(req, {
      batch: toSettlementBatchPayload(batch),
      entries: entries.map(toSettlementBatchEntryPayload),
      adjustments: adjustments.map(toSettlementBatchAdjustmentPayload),
      events: events.map(toSettlementBatchEventPayload)
    })
  );
});

router.get(
  "/settlement-batches/:batchId/export.csv",
  requireMerchantToken,
  async (req: MerchantRequest, res: Response, next: NextFunction) => {
    const batch = await getSettlementBatchByIdForMerchant(req.params.batchId, req.merchantId || "");
    if (!batch) {
      return next(new ApiError("NOT_FOUND", "settlement batch not found", 404));
    }
    const formatRaw = req.query.format as string | undefined;
    const format = parseSettlementExportFormat(formatRaw);
    if (!format) {
      return next(new ApiError("BAD_REQUEST", "format must be standard|bank_generic", 400));
    }

    const rows = await getSettlementBatchReportRows(batch.id);
    const csv = format === "bank_generic" ? settlementBatchReportToBankGenericCsv(rows) : settlementBatchReportToCsv(rows);
    const filename =
      format === "bank_generic"
        ? `${batch.batch_code}-bank-generic-payout.csv`
        : `${batch.batch_code}-payout-report.csv`;
    await createSettlementBatchEvent({
      batch_id: batch.id,
      merchant_id: batch.merchant_id,
      event_type: "csv_exported",
      actor_type: "merchant",
      actor_id: batch.merchant_id,
      payload_json: {
        format,
        report_rows: rows.length,
        filename
      }
    });
    res.setHeader("Content-Type", "text/csv; charset=utf-8");
    res.setHeader("Content-Disposition", `attachment; filename="${filename}"`);
    res.send(csv);
  }
);

export default router;
