// server.js

import express from 'express';
import dotenv from 'dotenv';
import path from 'path';
import { fileURLToPath } from 'url';
import apiRouter from './routes/api.js';
import session from 'express-session';
import passport from 'passport';
import { Strategy as GoogleStrategy } from 'passport-google-oauth20';

// NEW: Import Google API libraries
import { google } from 'googleapis';
import { OAuth2Client } from 'google-auth-library'; // For managing OAuth tokens

// Load environment variables from .env file
dotenv.config();

// --- CRITICAL: Ensure console logs are flushed immediately ---
// This can help ensure messages appear before a crash in Codespaces
process.stdout.uncork();
process.stderr.uncork();

// --- Environment Variable Checks ---
const requiredEnvVars = [
    'GOOGLE_API_KEY', // Your restricted API key for direct service access (if applicable)
    'GOOGLE_CLIENT_ID', // Your OAuth Client ID
    'GOOGLE_CLIENT_SECRET', // Your OAuth Client Secret
    'GOOGLE_REDIRECT_URI', // Your authorized redirect URI (e.g., https://ubiquitous-bassoon-jjg6wq44p9jr3q7j7-3001.app.github.dev/auth/google/callback)
    'SESSION_SECRET' // For express-session
];

for (const varName of requiredEnvVars) {
  if (!process.env[varName]) {
    console.error(`FATAL ERROR: Environment variable ${varName} is not set. Please check your .env file or Codespaces secrets.`);
    process.exit(1);
  }
}

// --- Catch unhandled errors ---
process.on('uncaughtException', (err, origin) => {
  console.error('FATAL UNCAUGHT EXCEPTION:', err);
  console.error('Exception origin:', origin);
  process.exit(1);
});

// --- Log process exit/termination ---
process.on('exit', (code) => {
  console.log(`Server process exited with code: ${code}`);
});
process.on('SIGTERM', () => {
  console.log('Server process received SIGTERM signal. Shutting down gracefully...');
  process.exit(0);
});


// Setup for ES module __dirname
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

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


// --- Google OAuth2Client Setup ---
// This client is used to initiate the OAuth flow and refresh tokens.
const oauth2Client = new OAuth2Client(
  process.env.GOOGLE_CLIENT_ID,
  process.env.GOOGLE_CLIENT_SECRET,
  process.env.GOOGLE_REDIRECT_URI
);

// In-memory user database (for demonstration purposes, replace with persistent DB in production)
const users = {};

// --- Passport.js Configuration ---
passport.use(new GoogleStrategy({
    clientID: process.env.GOOGLE_CLIENT_ID,
    clientSecret: process.env.GOOGLE_CLIENT_SECRET,
    callbackURL: process.env.GOOGLE_REDIRECT_URI, // Use the environment variable
    passReqToCallback: true // Allows us to access req in the callback
  },
  async (request, accessToken, refreshToken, profile, done) => {
    // Save tokens and profile info. In a real app, save to a DB.
    // For demonstration, we'll store basic info and tokens (NOT SECURE FOR PRODUCTION)
    users[profile.id] = {
      id: profile.id,
      name: profile.displayName,
      email: profile.emails[0].value,
      photo: profile.photos[0].value,
      accessToken: accessToken,    // Store for making API calls on behalf of the user
      refreshToken: refreshToken,  // Store for refreshing access tokens
    };
    console.log("Google profile received: " + profile.displayName);
    console.log("Access Token (store securely!): " + accessToken);

    // Set credentials for this user's OAuth2Client instance for immediate use
    oauth2Client.setCredentials({ access_token: accessToken, refresh_token: refreshToken });

    return done(null, users[profile.id]);
  }
));

// REVISED: Fix for 'ReferenceError: id is not defined'
passport.serializeUser((user, done) => {
  done(null, user.id);
});

passport.deserializeUser((id, done) => {
  // In a real app, retrieve user from database.
  done(null, users[id]);
});


// --- Middleware Setup ---
app.use(session({
  secret: process.env.SESSION_SECRET,
  resave: false,
  saveUninitialized: true,
  cookie: { secure: app.get('env') === 'production' } // 'true' for HTTPS in prod, 'false' for http in dev (Codespaces)
}));
app.use(passport.initialize());
app.use(passport.session());
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

// Serve static files from 'public' and 'node_modules'
app.use(express.static(path.join(__dirname, 'public')));
app.use('/node_modules', express.static(path.join(__dirname, 'node_modules')));

// --- Routes ---
app.get('/auth/google',
  passport.authenticate('google', { scope: [
    'profile',
    'email',
    'https://www.googleapis.com/auth/drive.readonly',
    'https://www.googleapis.com/auth/gmail.readonly',
    'https://www.googleapis.com/auth/calendar.readonly',
    'https://www.googleapis.com/auth/contacts.readonly', // Uses People API internally
    'https://www.googleapis.com/auth/photoslibrary.readonly'
  ] })); // Include all necessary scopes here

app.get('/auth/google/callback',
  passport.authenticate('google', { failureRedirect: '/' }),
  (req, res) => {
    console.log('Successfully authenticated! User:', req.user.name);
    // Redirect to the main application page or a dashboard
    res.redirect('/');
  }
);

app.get('/logout', (req, res, next) => {
    req.logout(err => {
        if (err) { return next(err); }
        // Clear tokens from in-memory store if applicable (for demo purposes)
        if (req.user && users[req.user.id]) {
            delete users[req.user.id].accessToken;
            delete users[req.user.id].refreshToken;
        }
        res.redirect('/');
    });
});

// --- NEW: API Endpoint to Handle Gemini Tool Calls from AI Studio (Conceptual) ---
// This is the core piece that connects AI Studio's function calls to your backend.
// In a real application, this would be a secure endpoint that:
// 1. Receives the tool call from AI Studio (after Gemini decides to call a function).
// 2. Executes the corresponding function using the Google APIs.
// 3. Returns the result back to AI Studio (which then feeds it back to Gemini).

app.post('/api/gemini-tool-call', async (req, res) => {
    if (!req.isAuthenticated()) {
        return res.status(401).json({ error: 'Unauthorized: User not logged in.' });
    }

    const { toolName, args } = req.body; // Expecting toolName and args from AI Studio's output

    console.log(`Received tool call: ${toolName} with args:`, args);

    let result;
    try {
        // Authenticate the OAuth2Client with the current user's tokens before making API calls
        // In a real app, retrieve user's tokens from a session store or database.
        const currentUser = users[req.user.id]; // Access token from in-memory users object
        if (!currentUser || !currentUser.accessToken) {
            throw new Error('User access token not found for API call.');
        }
        oauth2Client.setCredentials({
            access_token: currentUser.accessToken,
            refresh_token: currentUser.refreshToken // Include refresh token if available for long-lived sessions
        });

        switch (toolName) {
            case 'gmail_read_emails':
                result = await gmail_read_emails(args);
                break;
            case 'drive_search_files':
                result = await drive_search_files(args);
                break;
            case 'calendar_get_events':
                result = await calendar_get_events(args);
                break;
            case 'contacts_search':
                result = await contacts_search(args);
                break;
            case 'photos_search_media':
                result = await photos_search_media(args);
                break;
            default:
                throw new Error(`Unknown tool: ${toolName}`);
        }
        res.json({ success: true, data: result });
    } catch (error) {
        console.error(`Error executing tool ${toolName}:`, error.message, error.stack);
        res.status(500).json({ success: false, error: error.message });
    }
});


// --- NEW: Functions to interact with Google APIs (matching AI Studio declarations) ---
// These functions use the oauth2Client initialized with the user's tokens.

async function gmail_read_emails(args) {
    if (!oauth2Client.credentials.access_token) throw new Error('Access token not available for Gmail API.');
    const gmail = google.gmail({ version: 'v1', auth: oauth2Client });
    const queryParts = [];
    if (args.subject) queryParts.push(`subject:(${args.subject})`);
    if (args.sender) queryParts.push(`from:(${args.sender})`);
    if (args.label) queryParts.push(`label:(${args.label})`);
    if (args.keywords) queryParts.push(args.keywords);
    
    const q = queryParts.join(' ');
    
    console.log(`Calling Gmail API with query: "${q}", maxResults: ${args.maxResults}`);
    const res = await gmail.users.messages.list({
        userId: 'me',
        q: q,
        maxResults: args.maxResults || 5,
        fields: 'messages(id,internalDate,payload(headers))'
    });
    
    const messages = res.data.messages || [];
    const detailedMessages = await Promise.all(messages.map(async (message) => {
        const msg = await gmail.users.messages.get({ userId: 'me', id: message.id, format: 'metadata', fields: 'snippet,payload(headers)' });
        const headers = msg.data.payload.headers;
        const subject = headers.find(header => header.name === 'Subject')?.value;
        const from = headers.find(header => header.name === 'From')?.value;
        return {
            id: message.id,
            subject: subject,
            from: from,
            snippet: msg.data.snippet
        };
    }));
    return detailedMessages;
}

async function drive_search_files(args) {
    if (!oauth2Client.credentials.access_token) throw new Error('Access token not available for Drive API.');
    const drive = google.drive({ version: 'v3', auth: oauth2Client });
    
    const queryParts = ["trashed = false"];
    if (args.keywords) queryParts.push(`(name contains '${args.keywords}' or fullText contains '${args.keywords}')`);
    if (args.fileType) queryParts.push(`mimeType contains 'application/vnd.google-apps.${args.fileType}'`);
    if (args.folderName) {
        console.warn("Drive API folderName search is simplified. Consider using folder IDs for precision.");
        queryParts.push(`'${args.folderName}' in parents`);
    }

    const q = queryParts.join(' and ');
    console.log(`Calling Drive API with query: "${q}", maxResults: ${args.maxResults}`);

    const res = await drive.files.list({
        q: q,
        pageSize: args.maxResults || 5,
        fields: 'files(id, name, mimeType, webContentLink, parents)',
    });
    return res.data.files;
}

async function calendar_get_events(args) {
    if (!oauth2Client.credentials.access_token) throw new Error('Access token not available for Calendar API.');
    const calendar = google.calendar({ version: 'v3', auth: oauth2Client });
    
    const timeMin = args.timeMin ? new Date(args.timeMin).toISOString() : (new Date()).toISOString();
    const timeMax = args.timeMax ? new Date(args.timeMax).toISOString() : new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString(); // Default to next 7 days

    console.log(`Calling Calendar API from ${timeMin} to ${timeMax} with keywords: "${args.keywords}", maxResults: ${args.maxResults}`);
    const res = await calendar.events.list({
        calendarId: 'primary',
        timeMin: timeMin,
        timeMax: timeMax,
        q: args.keywords,
        maxResults: args.maxResults || 5,
        singleEvents: true,
        orderBy: 'startTime',
    });
    return res.data.items;
}

async function contacts_search(args) {
    if (!oauth2Client.credentials.access_token) throw new Error('Access token not available for People API.');
    const people = google.people({ version: 'v1', auth: oauth2Client });
    
    console.log(`Calling People API with query: "${args.query}", maxResults: ${args.maxResults}`);
    const res = await people.people.connections.list({
        resourceName: 'people/me',
        personFields: 'names,emailAddresses,phoneNumbers',
        query: args.query,
        pageSize: args.maxResults || 5,
    });
    const connections = res.data.connections || [];
    return connections.map(person => ({
        displayName: person.names && person.names.length > 0 ? person.names[0].displayName : 'N/A',
        email: person.emailAddresses && person.emailAddresses.length > 0 ? person.emailAddresses[0].value : 'N/A',
        phone: person.phoneNumbers && person.phoneNumbers.length > 0 ? person.phoneNumbers[0].value : 'N/A',
    }));
}

async function photos_search_media(args) {
    if (!oauth2Client.credentials.access_token) throw new Error('Access token not available for Photos Library API.');
    const photoslibrary = google.photoslibrary({ version: 'v1', auth: oauth2Client });

    const filters = {};
    if (args.keywords) {
        console.warn("Photos Library API keyword search is highly simplified. Complex keyword mapping needed.");
    }
    if (args.startDate && args.endDate) {
        filters.dateFilter = {
            ranges: [{
                startDate: { year: parseInt(args.startDate.substring(0,4)), month: parseInt(args.startDate.substring(5,7)), day: parseInt(args.startDate.substring(8,10)) },
                endDate: { year: parseInt(args.endDate.substring(0,4)), month: parseInt(args.endDate.substring(5,7)), day: parseInt(args.endDate.substring(8,10)) }
            }]
        };
    }

    console.log(`Calling Photos Library API with filters:`, filters, `maxResults: ${args.maxResults}`);
    const res = await photoslibrary.mediaItems.search({
        filters: filters,
        pageSize: args.maxResults || 5,
    });
    const mediaItems = res.data.mediaItems || [];
    return mediaItems.map(item => ({
        id: item.id,
        filename: item.filename,
        baseUrl: item.baseUrl,
        mediaMetadata: item.mediaMetadata,
    }));
}


// --- API Router for other custom endpoints ---
app.use('/api', apiRouter);

// --- Start Server ---
app.listen(PORT, () => {
  console.log(`🚀 Server is running on http://localhost:${PORT}`);
});