import path from 'path';
import fs from 'fs';
import dotenv from 'dotenv';

// Load .env from this package first, then root
dotenv.config({ path: path.resolve(__dirname, '../.env') });
dotenv.config({ path: path.resolve(__dirname, '../../../.env') });

import express from 'express';
import cors from 'cors';
import helmet from 'helmet';
import morgan from 'morgan';
import rateLimit from 'express-rate-limit';

import { errorHandler } from './middleware/errorHandler';
import { authRouter } from './routes/auth';
import { propertiesRouter } from './routes/properties';
import { leadsRouter } from './routes/leads';
import { subscriptionsRouter } from './routes/subscriptions';
import { webinarsRouter } from './routes/webinars';
import { usersRouter } from './routes/users';
import { agenciesRouter } from './routes/agencies';
import { developersRouter } from './routes/developers';
import { webhooksRouter } from './routes/webhooks';
import { scraperRouter } from './routes/scraper';
import { adminRouter } from './routes/admin';
import { seoRouter } from './routes/seo';
import { internalRouter } from './routes/internal';
import { iosV1Router }    from './routes/ios';
import { submissionsRouter } from './routes/submissions';
import { chatRouter } from './routes/chat';

const app = express();
const PORT = Number(process.env.PORT) || 3001;

// Force all responses to be JSON
app.use((_req, res, next) => {
  res.setHeader('Content-Type', 'application/json');
  next();
});

// Security middleware
app.use(helmet({
  contentSecurityPolicy: false, // Allow dev
}));

app.use(cors({
  origin: (origin, callback) => {
    const allowed = [
      'http://localhost:3002',
      'http://localhost:3000',
      'http://192.168.18.220:3002',
      'http://192.168.18.220:3000',
      'http://127.0.0.1:3002',
      ...(process.env.ALLOWED_ORIGINS ? process.env.ALLOWED_ORIGINS.split(',') : []),
      ...(process.env.NEXT_PUBLIC_APP_URL ? [process.env.NEXT_PUBLIC_APP_URL] : [])
    ];
    // Allow request with no origin (Postman, curl) or if origin is in whitelist or is a Vercel preview link
    if (!origin || allowed.includes(origin) || origin.endsWith('.vercel.app')) {
      callback(null, true);
    } else {
      callback(new Error(`CORS blocked: ${origin}`));
    }
  },
  credentials: true,       // ← REQUIRED for cookie forwarding
  methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'],
  allowedHeaders: ['Content-Type', 'Authorization', 'Cookie', 'X-Requested-With'],
  exposedHeaders: ['Set-Cookie'],
}));

// Handle preflight for all routes
app.options('*', cors());

// Rate limiting
const limiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: process.env.NODE_ENV === 'development' ? 100000 : 1000,
  standardHeaders: true,
  legacyHeaders: false,
  skip: (req) => {
    const ip = req.ip || '';
    return ip.includes('127.0.0.1') || ip.includes('::1') || ip === 'localhost';
  },
  handler: (_req, res, _next, options) => {
    res.status(options.statusCode).json({
      success: false,
      error: options.message || 'Too many requests, please slow down.'
    });
  }
});
app.use(limiter);

// Stricter limit for auth endpoints
const authLimiter = rateLimit({
  windowMs: 15 * 60 * 1000,
  max: process.env.NODE_ENV === 'development' ? 100000 : 5,
  skipSuccessfulRequests: true,
  skip: (req) => {
    const ip = req.ip || '';
    return ip.includes('127.0.0.1') || ip.includes('::1') || ip === 'localhost';
  },
  handler: (_req, res, _next, options) => {
    res.status(options.statusCode).json({
      success: false,
      error: options.message || 'Too many requests, please slow down.'
    });
  }
});

// Logging
app.use(morgan('combined'));

// Stripe Webhook needs raw body for signature verification.
// Register this BEFORE express.json() body parsing middleware.
app.use('/webhooks', express.raw({ type: 'application/json' }), webhooksRouter);

// Body parsing
app.use(express.json({ limit: '10mb' }));
app.use(express.urlencoded({ extended: true, limit: '10mb' }));

// Static serving for uploads
const uploadsDir = path.join(__dirname, '../uploads');
if (!fs.existsSync(uploadsDir)) {
  fs.mkdirSync(uploadsDir, { recursive: true });
}
app.use('/uploads', (_req, res, next) => {
  res.setHeader('Access-Control-Allow-Origin', '*');
  res.setHeader('Cross-Origin-Resource-Policy', 'cross-origin');
  
  const filePath = _req.path;
  const ext = path.extname(filePath).toLowerCase();
  const mimeTypes: Record<string, string> = {
    '.png': 'image/png',
    '.jpg': 'image/jpeg',
    '.jpeg': 'image/jpeg',
    '.gif': 'image/gif',
    '.svg': 'image/svg+xml',
    '.webp': 'image/webp',
    '.ico': 'image/x-icon'
  };
  const type = mimeTypes[ext];
  if (type) {
    res.setHeader('Content-Type', type);
  }
  next();
}, express.static(uploadsDir));

// ─── Health check ──────────────────────────────────────────
app.get('/health', (_req, res) => {
  res.json({ status: 'ok', timestamp: new Date().toISOString() });
});

// ─── API Routes — both /auth and /api/auth prefixes ────────
// Direct routes (for axios calls from frontend lib/api.ts using base /api)
app.use('/auth', authLimiter, authRouter);
app.use('/properties', propertiesRouter);
app.use('/leads', leadsRouter);
app.use('/subscriptions', subscriptionsRouter);
app.use('/webinars', webinarsRouter);
app.use('/users', usersRouter);
app.use('/agencies', agenciesRouter);
app.use('/developers', developersRouter);

app.use('/submissions', submissionsRouter);
app.use('/contact', submissionsRouter);
app.use('/api/submissions', submissionsRouter);
app.use('/api/contact', submissionsRouter);

app.use('/chat', chatRouter);
app.use('/api/chat', chatRouter);

// /api/* prefixed routes (for direct curl / proxy calls)
app.use('/api/auth', authLimiter, authRouter);
app.use('/api/properties', propertiesRouter);
app.use('/api/leads', leadsRouter);
app.use('/api/subscriptions', subscriptionsRouter);
app.use('/api/webinars', webinarsRouter);
app.use('/api/users', usersRouter);
app.use('/api/agencies', agenciesRouter);
app.use('/api/developers', developersRouter);

// ─── Admin Scraper endpoints ───────────────────────────────
app.use('/api/admin', scraperRouter);
app.use('/api/admin', adminRouter);

// ─── SEO Metadata endpoints ─────────────────────────────
app.use('/api/seo', seoRouter);
app.use('/api/admin/seo', seoRouter);
app.use('/api/internal', internalRouter);

// ─── iOS Headless CMS — versioned, fully isolated ──────────────────────────
// Mounted at BOTH paths because the Next.js afterFiles rewrite strips
// the leading /api from the path before forwarding to Express.
// • Direct Express calls:  /api/v1/ios/*   (curl, Postman, iOS app directly)
// • Via Next.js proxy:     /v1/ios/*       (admin frontend using fetchWithAuth)
app.use('/api/v1/ios', iosV1Router);
app.use('/v1/ios',     iosV1Router);

// ─── Meta / Filter endpoint ────────────────────────────────
import { pool } from './config/database';

app.get('/api/meta/filters', async (_req, res) => {
  try {
    const [locations, propertyTypes, projects] = await Promise.all([
      pool.execute('SELECT id, name, slug FROM locations WHERE is_active = 1 ORDER BY name ASC'),
      pool.execute('SELECT id, name, display_name, category FROM property_types WHERE is_active = 1 ORDER BY sort_order ASC'),
      pool.execute("SELECT id, name, slug FROM projects WHERE status = 'active' AND deleted_at IS NULL ORDER BY name ASC"),
    ]);
    res.json({
      success: true,
      data: {
        locations: (locations[0] as any[]),
        propertyTypes: (propertyTypes[0] as any[]),
        projects: (projects[0] as any[]),
      },
    });
  } catch (err: any) {
    res.status(500).json({ success: false, error: err.message });
  }
});

// ─── Admin Dashboard Stats ─────────────────────────────────
app.get('/api/dashboard/stats', async (_req, res) => {
  try {
    const [[usersRow], [unitsRow], [locationsRow], [inquiriesRow]] = await Promise.all([
      pool.execute('SELECT COUNT(*) as count FROM users WHERE deleted_at IS NULL'),
      pool.execute('SELECT COUNT(*) as count FROM units WHERE is_active = 1 AND deleted_at IS NULL'),
      pool.execute('SELECT COUNT(*) as count FROM locations WHERE is_active = 1'),
      pool.execute('SELECT COUNT(*) as count FROM property_inquiries'),
    ]);
    res.json({
      success: true,
      data: {
        total_users: (usersRow as any[])[0]?.count ?? 0,
        total_properties: (unitsRow as any[])[0]?.count ?? 0,
        total_locations: (locationsRow as any[])[0]?.count ?? 0,
        total_inquiries: (inquiriesRow as any[])[0]?.count ?? 0,
      },
    });
  } catch (err: any) {
    res.status(500).json({ success: false, error: err.message });
  }
});

// ─── Partners Endpoint ─────────────────────────────────────
app.get(['/api/v1/public/partners', '/v1/public/partners', '/api/partners'], async (_req, res) => {
  try {
    const [rows] = await pool.execute(
      `SELECT 
        u.id, 
        TRIM(CONCAT(u.first_name, ' ', u.last_name)) AS name, 
        u.avatar_url AS logo_url,
        r.name AS role
       FROM users u
       JOIN roles r ON u.primary_role_id = r.id
       WHERE r.name IN ('developer', 'agency') AND u.status = 'active'
       ORDER BY u.created_at ASC`
    );
    res.json({
      success: true,
      data: rows,
    });
  } catch (err: any) {
    res.status(500).json({ success: false, error: err.message });
  }
});

// ─── Error Handling — MUST be last ─────────────────────────
app.use(errorHandler);

// 404 handler — always JSON
app.use((req: any, res: any) => {
  res.status(404).json({
    success: false,
    error: 'Endpoint not found',
    message: `Route ${req.method} ${req.path} not found`,
    data: null,
  });
});

// Global uncaught error handler
app.use((err: any, _req: any, res: any, _next: any) => {
  console.error('Unhandled Error:', err);
  res.status(err.status || err.statusCode || 500).json({
    success: false,
    message: err.message || 'Internal server error',
    data: null,
  });
});

async function initDbSchema() {
  try {
    const [columns] = await pool.query("SHOW COLUMNS FROM users LIKE 'reset_token'");
    if ((columns as any[]).length === 0) {
      console.log('📊 Schema update: adding reset_token columns to users table...');
      await pool.query(`
        ALTER TABLE users
        ADD COLUMN reset_token VARCHAR(255) NULL,
        ADD COLUMN reset_token_expires_at TIMESTAMP NULL
      `);
      console.log('📊 Schema update complete: reset_token columns added.');
    }
  } catch (err) {
    console.error('📊 Schema update failed:', err);
  }
}

app.listen(PORT, '0.0.0.0', async () => {
  await initDbSchema();
  console.log(`🚀 API Server running on port ${PORT} (0.0.0.0)`);
  console.log(`📊 DB: ${process.env.DB_HOST}:${process.env.DB_PORT}/${process.env.DB_NAME}`);
});

export default app;
