import 'dotenv/config';
import fs from 'fs';
import path from 'path';
import express from 'express';
import type { ErrorRequestHandler } from 'express';
import cors from 'cors';
import { checkDatabaseConnection, closeDatabaseConnections } from './config/db';
import {
  allowLocalFallback,
  allowedOrigins,
  appEnvironment,
  backendPort,
  isProductionEnvironment,
  fallbackPolicyLabel,
  frontendDistPath,
  serveFrontend,
  trustProxy,
} from './config/env';
import availabilityRoutes from './routes/availability.routes';
import authRoutes from './routes/auth.routes';
import appointmentRoutes from './routes/appointment.routes';
import budgetRoutes from './routes/budget.routes';
import financeRoutes from './routes/finance.routes';
import patientAuthRoutes from './routes/patient-auth.routes';
import inventoryRoutes from './routes/inventory.routes';
import patientRoutes from './routes/patient.routes';
import patientPortalRoutes from './routes/patient-portal.routes';
import dashboardRoutes from './routes/dashboard.routes';
import doctorMobileRoutes from './routes/doctor-mobile.routes';
import documentRoutes from './routes/document.routes';
import notificationRoutes from './routes/notification.routes';
import recipeRoutes from './routes/recipe.routes';
import reportRoutes from './routes/report.routes';
import settingsRoutes from './routes/settings.routes';
import treatmentCatalogRoutes from './routes/treatment-catalog.routes';
import { LocalFallbackDisabledError } from './services/local-store';
import agentDentalKnowledgeRoutes from './routes/agent-dental-knowledge.routes';
import { startAppointmentReminderScheduler } from './services/appointment-reminder.service';

export const app = express();
const port = backendPort;
const resolvedFrontendDistPath = frontendDistPath
  ? path.isAbsolute(frontendDistPath)
    ? frontendDistPath
    : path.resolve(__dirname, frontendDistPath)
  : path.resolve(__dirname, '../../frontend/dist');
const frontendIndexPath = path.join(resolvedFrontendDistPath, 'index.html');
const frontendBuildAvailable = fs.existsSync(frontendIndexPath);

app.disable('x-powered-by');

if (trustProxy) {
  app.set('trust proxy', 1);
}

const corsMiddleware = cors({
  origin(origin, callback) {
    if (!origin) {
      callback(null, true);
      return;
    }

    if (allowedOrigins.length === 0) {
      callback(null, true);
      return;
    }

    if (allowedOrigins.includes(origin)) {
      callback(null, true);
      return;
    }

    callback(new Error(`Origen no permitido por CORS: ${origin}`));
  },
});

// Middlewares
app.use(corsMiddleware);
app.use(express.json({ limit: '25mb' }));

if (serveFrontend && frontendBuildAvailable) {
  app.use(express.static(resolvedFrontendDistPath));
} else if (serveFrontend) {
  console.warn(`SERVE_FRONTEND=true pero no se encontro un build en ${resolvedFrontendDistPath}`);
}

// Main Root endpoint
app.get('/', (req, res) => {
  if (serveFrontend && frontendBuildAvailable) {
    return res.sendFile(frontendIndexPath);
  }

  return res.json({ message: 'La API de DentaFlow esta operativa.' });
});

app.get('/health', async (_req, res) => {
  const database = await checkDatabaseConnection();

  return res.status(200).json({
    status: 'ok',
    environment: appEnvironment,
    fallbackPolicy: fallbackPolicyLabel,
    database: {
      ok: database.ok,
      configured: true,
      error: database.error,
    },
  });
});

app.get('/health/ready', async (_req, res) => {
  const database = await checkDatabaseConnection();
  const isReady = database.ok || allowLocalFallback;

  return res.status(isReady ? 200 : 503).json({
    status: isReady ? 'ready' : 'not-ready',
    environment: appEnvironment,
    fallbackPolicy: fallbackPolicyLabel,
    database: {
      ok: database.ok,
      error: database.error,
    },
    reason:
      isReady ? null : 'La base de datos principal no esta disponible y ALLOW_LOCAL_FALLBACK esta deshabilitado.',
  });
});

// Routes
app.use('/api/auth', authRoutes);
app.use('/api/appointments', appointmentRoutes);
app.use('/api/availability', availabilityRoutes);
app.use('/api/budgets', budgetRoutes);
app.use('/api/finances', financeRoutes);
app.use('/api/patient-auth', patientAuthRoutes);
app.use('/api/inventory', inventoryRoutes);
app.use('/api/patients', patientRoutes);
app.use('/api/patient-portal', patientPortalRoutes);
app.use('/api/dashboard', dashboardRoutes);
app.use('/api/doctor-mobile', doctorMobileRoutes);
app.use('/api/documents', documentRoutes);
app.use('/api/notifications', notificationRoutes);
app.use('/api/recipes', recipeRoutes);
app.use('/api/reports', reportRoutes);
app.use('/api/settings', settingsRoutes);
app.use('/api/treatment-catalog', treatmentCatalogRoutes);
app.use('/api/agent-dental/knowledge', agentDentalKnowledgeRoutes);

if (serveFrontend && frontendBuildAvailable) {
  app.get(/^\/(?!api(?:\/|$)|health(?:\/|$)).*/, (_req, res) => {
    res.sendFile(frontendIndexPath);
  });
}

const errorHandler: ErrorRequestHandler = (error, _req, res, _next) => {
  if (error instanceof LocalFallbackDisabledError) {
    return res.status(503).json({
      error: error.message,
      fallbackPolicy: fallbackPolicyLabel,
    });
  }

  if (error && typeof error === 'object' && 'type' in error && error.type === 'entity.too.large') {
    return res.status(413).json({ error: 'El archivo es demasiado grande. Intenta con uno de menor tamano.' });
  }

  console.error('Unhandled server error:', error);
  return res.status(500).json({ error: 'Error interno del servidor' });
};

app.use(errorHandler);

export const startServer = () =>
  app.listen(port, () => {
    startAppointmentReminderScheduler();
    console.log(`Server is running on port ${port}`);
    if (serveFrontend && frontendBuildAvailable) {
      console.log(`Serving frontend build from ${resolvedFrontendDistPath}`);
    }
    if (allowedOrigins.length > 0) {
      console.log(`CORS restringido a ${allowedOrigins.length} origen(es) configurados.`);
    } else if (isProductionEnvironment) {
      console.warn('ALLOWED_ORIGINS no configurado; CORS permanecera abierto para clientes web.');
    }
  });

if (require.main === module) {
  const server = startServer();

  const shutdown = async (signal: string) => {
    console.log(`Recibido ${signal}. Cerrando servidor HTTP y conexiones de base de datos...`);

    server.close(async () => {
      await closeDatabaseConnections().catch((error) => {
        console.error('Error cerrando conexiones de base de datos:', error);
      });
      process.exit(0);
    });
  };

  process.on('SIGINT', () => {
    void shutdown('SIGINT');
  });

  process.on('SIGTERM', () => {
    void shutdown('SIGTERM');
  });
}
