import express, { Express, Request, Response, NextFunction } from 'express';
import cors from 'cors';
import helmet from 'helmet';
import dotenv from 'dotenv';
import path from 'path';
import cookieParser from 'cookie-parser';
import { Readable } from 'stream';
import { GetObjectCommand } from '@aws-sdk/client-s3';
import { s3Client, BUCKET_NAME } from './utils/s3';

dotenv.config();

import { errorHandler } from './middleware/error.middleware';
import authRoutes from './routes/auth.routes';
import assetRoutes from './routes/asset.routes';
import orgRoutes from './routes/organization.routes';
import legalRoutes from './routes/legal.routes';
import ecosystemRoutes from './routes/ecosystem.routes';

import { ensureBucketExists } from './utils/s3';
import { originStorage } from './utils/origin-storage';

const app: Express = express();
const PORT = process.env.PORT || 5000;

app.use(helmet({
  crossOriginResourcePolicy: { policy: "cross-origin" },
  contentSecurityPolicy: {
    useDefaults: false,
    directives: {
      "default-src": helmet.contentSecurityPolicy.dangerouslyDisableDefaultSrc,
      "frame-ancestors": ["'self'", "http://localhost:5173", "http://localhost:5000", "https://*.ngrok-free.dev", "https://*.ngrok.io"],
    },
  },
  frameguard: false,
}));
app.use(cors({ origin: true, credentials: true }));
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(cookieParser());

// Capture request origin and run in AsyncLocalStorage context
app.use((req: Request, res: Response, next: NextFunction) => {
  let clientOrigin: string | undefined;
  const originHeader = req.get('origin');
  if (originHeader) {
    clientOrigin = originHeader;
  } else {
    const refererHeader = req.get('referer');
    if (refererHeader) {
      try {
        clientOrigin = new URL(refererHeader).origin;
      } catch {
        // ignore invalid urls
      }
    }
  }
  
  if (clientOrigin) {
    originStorage.run(clientOrigin, next);
  } else {
    next();
  }
});

// Dynamic MinIO object delivery handler
app.get('/uploads/:filename', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const filename = req.params.filename;
    const command = new GetObjectCommand({
      Bucket: BUCKET_NAME,
      Key: filename,
    });

    const s3Response = await s3Client.send(command);
    
    if (s3Response.ContentType) {
      res.setHeader('Content-Type', s3Response.ContentType);
    }
    if (s3Response.ContentLength) {
      res.setHeader('Content-Length', s3Response.ContentLength);
    }
    res.setHeader('Accept-Ranges', 'bytes');

    if (s3Response.Body instanceof Readable) {
      s3Response.Body.pipe(res);
    } else if (s3Response.Body) {
      const body = s3Response.Body as any;
      if (typeof body.pipe === 'function') {
        body.pipe(res);
      } else {
        res.send(await s3Response.Body.transformToByteArray());
      }
    } else {
      res.status(404).send('Not found');
    }
  } catch (err: any) {
    if (err.name === 'NoSuchKey' || err.$metadata?.httpStatusCode === 404) {
      res.status(404).send('File not found in storage');
    } else {
      next(err);
    }
  }
});

// API Routes
app.use('/api/v1/auth', authRoutes);
app.use('/api/v1/assets', assetRoutes);
app.use('/api/v1/organizations', orgRoutes);
app.use('/api/v1/legal', legalRoutes);
app.use('/api/v1/ecosystem', ecosystemRoutes);

app.get('/api/v1/health', (req: Request, res: Response) => {
  res.status(200).json({ status: 'success', message: 'API is fully functional and real.' });
});

app.use(errorHandler);

// Monolithic approach: Serve React build in production
if (process.env.NODE_ENV === 'production') {
  const clientBuildPath = path.join(__dirname, '../../Channel-Frontend/dist');
  app.use(express.static(clientBuildPath));

  app.get('*', (req: Request, res: Response) => {
    res.sendFile(path.join(clientBuildPath, 'index.html'));
  });
}

app.listen(PORT, async () => {
  console.log(`[Server]: API is running on http://localhost:${PORT}`);
  await ensureBucketExists();
});
