import express from 'express';
import cors from 'cors';
import { createClient } from '@libsql/client';
import multer from 'multer';
import path from 'path';
import { fileURLToPath } from 'url';
import fs from 'fs';
import cron from 'node-cron';
import { checkMowingReminders, sendWeeklyReport, testTelegramConnection } from './telegram.js';
import dotenv from 'dotenv';

dotenv.config({
  path: process.env.NODE_ENV === 'production' ? '.env.production' : '.env.development',
});

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

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

// Middleware
app.use(cors());
app.use(express.json());

// Serve static files in production
if (process.env.NODE_ENV === 'production') {
  app.use(express.static(path.join(__dirname, '../public')));
}

// Serve uploads
app.use('/uploads', express.static('uploads'));

// Ensure uploads directory exists
const uploadsDir = 'uploads';
if (!fs.existsSync(uploadsDir)) {
  fs.mkdirSync(uploadsDir);
}

// Configure multer for file uploads
const storage = multer.diskStorage({
  destination: (req, file, cb) => {
    cb(null, 'uploads/');
  },
  filename: (req, file, cb) => {
    const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9);
    cb(null, file.fieldname + '-' + uniqueSuffix + path.extname(file.originalname));
  }
});

const upload = multer({ 
  storage: storage,
  fileFilter: (req, file, cb) => {
    if (file.mimetype.startsWith('image/')) {
      cb(null, true);
    } else {
      cb(new Error('Only image files are allowed!'), false);
    }
  },
  limits: {
    fileSize: 5 * 1024 * 1024 // 5MB limit
  }
});

// Initialize libsql database
const db = createClient({
  url: 'file:db/lawn_scheduler.db'
});

// Create mowers table
await db.execute(`
  CREATE TABLE IF NOT EXISTS mowers (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT NOT NULL,
    type TEXT NOT NULL,
    brand TEXT,
    model TEXT,
    isActive BOOLEAN DEFAULT 1,
    createdAt TEXT DEFAULT CURRENT_TIMESTAMP
  )
`);

// Create zones table with updated schema
await db.execute(`
  CREATE TABLE IF NOT EXISTS zones (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT NOT NULL,
    imagePath TEXT,
    lastMowedDate TEXT,
    intervalDays INTEGER,
    nextMowDate TEXT,
    scheduleType TEXT DEFAULT 'interval',
    area REAL DEFAULT 0,
    createdAt TEXT DEFAULT CURRENT_TIMESTAMP
  )
`);

// Create mowing history table
await db.execute(`
  CREATE TABLE IF NOT EXISTS mowing_history (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    zoneId INTEGER NOT NULL,
    mowerId INTEGER,
    mowedDate TEXT NOT NULL,
    notes TEXT,
    duration INTEGER, -- in minutes
    weather TEXT,
    activityType TEXT DEFAULT 'mowing', -- 'mowing' or 'trimming'
    sessionId TEXT, -- for grouping bulk mowing sessions
    createdAt TEXT DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (zoneId) REFERENCES zones (id) ON DELETE CASCADE,
    FOREIGN KEY (mowerId) REFERENCES mowers (id) ON DELETE SET NULL
  )
`);

// Create season settings table
await db.execute(`
  CREATE TABLE IF NOT EXISTS season_settings (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    startMonth INTEGER NOT NULL DEFAULT 5,
    startDay INTEGER NOT NULL DEFAULT 1,
    endMonth INTEGER NOT NULL DEFAULT 9,
    endDay INTEGER NOT NULL DEFAULT 30,
    isActive BOOLEAN DEFAULT 1,
    createdAt TEXT DEFAULT CURRENT_TIMESTAMP,
    updatedAt TEXT DEFAULT CURRENT_TIMESTAMP
  )
`);

// Insert default season settings if table is empty
const seasonCountResult = await db.execute('SELECT COUNT(*) as count FROM season_settings');
const seasonCount = seasonCountResult.rows[0].count;

if (seasonCount === 0) {
  await db.execute({
    sql: 'INSERT INTO season_settings (startMonth, startDay, endMonth, endDay, isActive) VALUES (?, ?, ?, ?, ?)',
    args: [5, 1, 9, 30, 1] // May 1 to September 30, active
  });
}

// Add new columns to existing zones table if they don't exist
try {
  await db.execute(`ALTER TABLE zones ADD COLUMN nextMowDate TEXT`);
} catch (error) {
  // Column already exists, ignore error
}

try {
  await db.execute(`ALTER TABLE zones ADD COLUMN scheduleType TEXT DEFAULT 'interval'`);
} catch (error) {
  // Column already exists, ignore error
}

// Add area column to existing table if it doesn't exist
try {
  await db.execute(`ALTER TABLE zones ADD COLUMN area REAL DEFAULT 0`);
} catch (error) {
  // Column already exists, ignore error
}

// Add mowerId column to existing mowing_history table if it doesn't exist
try {
  await db.execute(`ALTER TABLE mowing_history ADD COLUMN mowerId INTEGER REFERENCES mowers (id) ON DELETE SET NULL`);
} catch (error) {
  // Column already exists, ignore error
}

// Add sessionId column to existing mowing_history table if it doesn't exist
try {
  await db.execute(`ALTER TABLE mowing_history ADD COLUMN sessionId TEXT`);
} catch (error) {
  // Column already exists, ignore error
}

// Add activityType column to existing mowing_history table if it doesn't exist
try {
  await db.execute(`ALTER TABLE mowing_history ADD COLUMN activityType TEXT DEFAULT 'mowing'`);
} catch (error) {
  // Column already exists, ignore error
}

// Insert default mowers if table is empty
const mowerCountResult = await db.execute('SELECT COUNT(*) as count FROM mowers');
const mowerCount = mowerCountResult.rows[0].count;

if (mowerCount === 0) {
  await db.execute({
    sql: 'INSERT INTO mowers (name, type, brand, model) VALUES (?, ?, ?, ?)',
    args: ['Battery Mower', 'Battery', 'Generic', 'Battery Model']
  });
  
  await db.execute({
    sql: 'INSERT INTO mowers (name, type, brand, model) VALUES (?, ?, ?, ?)',
    args: ['Electric Mower', 'Electric', 'Generic', 'Electric Model']
  });
}

// Insert sample data if table is empty
const countResult = await db.execute('SELECT COUNT(*) as count FROM zones');
const count = countResult.rows[0].count;

if (count === 0) {
  const today = new Date();
  const weekAgo = new Date(today.getTime() - 7 * 24 * 60 * 60 * 1000);
  const twoWeeksAgo = new Date(today.getTime() - 14 * 24 * 60 * 60 * 1000);
  
  // Get mower IDs for sample data
  const mowersResult = await db.execute('SELECT id FROM mowers LIMIT 2');
  const batteryMowerId = mowersResult.rows[0]?.id || 1;
  const electricMowerId = mowersResult.rows[1]?.id || 2;
  
  // Insert zones with different schedule types
  const frontYardResult = await db.execute({
    sql: 'INSERT INTO zones (name, lastMowedDate, intervalDays, scheduleType, area) VALUES (?, ?, ?, ?, ?)',
    args: ['Front Yard', weekAgo.toISOString(), 7, 'interval', 150.5]
  });
  
  const backYardResult = await db.execute({
    sql: 'INSERT INTO zones (name, lastMowedDate, intervalDays, scheduleType, area) VALUES (?, ?, ?, ?, ?)',
    args: ['Back Yard', twoWeeksAgo.toISOString(), 10, 'interval', 280.0]
  });
  
  const nextWeek = new Date(today.getTime() + 7 * 24 * 60 * 60 * 1000);
  const sideGardenResult = await db.execute({
    sql: 'INSERT INTO zones (name, lastMowedDate, nextMowDate, scheduleType, area) VALUES (?, ?, ?, ?, ?)',
    args: ['Side Garden', today.toISOString(), nextWeek.toISOString(), 'specific', 75.25]
  });

  // Insert sample mowing history with mower assignments
  const sampleHistory = [
    { zoneId: frontYardResult.lastInsertRowid, mowerId: batteryMowerId, date: today, duration: 45, weather: 'Sunny', notes: 'Perfect mowing conditions today' },
    { zoneId: frontYardResult.lastInsertRowid, mowerId: electricMowerId, date: weekAgo, duration: 40, weather: 'Cloudy', notes: 'Grass was a bit wet from morning dew' },
    { zoneId: backYardResult.lastInsertRowid, mowerId: electricMowerId, date: new Date(today.getTime() - 2 * 24 * 60 * 60 * 1000), duration: 60, weather: 'Partly cloudy', notes: 'Trimmed edges and cleaned up leaves' },
    { zoneId: sideGardenResult.lastInsertRowid, mowerId: batteryMowerId, date: new Date(today.getTime() - 3 * 24 * 60 * 60 * 1000), duration: 25, weather: 'Sunny', notes: 'Quick touch-up, looks great' },
    { zoneId: frontYardResult.lastInsertRowid, mowerId: batteryMowerId, date: new Date(today.getTime() - 14 * 24 * 60 * 60 * 1000), duration: 50, weather: 'Overcast', notes: 'First mow of the season' },
    { zoneId: backYardResult.lastInsertRowid, mowerId: electricMowerId, date: new Date(today.getTime() - 16 * 24 * 60 * 60 * 1000), duration: 65, weather: 'Sunny', notes: 'Had to go slow due to thick growth' },
    { zoneId: sideGardenResult.lastInsertRowid, mowerId: batteryMowerId, date: new Date(today.getTime() - 17 * 24 * 60 * 60 * 1000), duration: 30, weather: 'Windy', notes: 'Challenging conditions but got it done' },
    { zoneId: frontYardResult.lastInsertRowid, mowerId: electricMowerId, date: new Date(today.getTime() - 21 * 24 * 60 * 60 * 1000), duration: 42, weather: 'Cool', notes: 'Nice cool morning for mowing' },
    { zoneId: backYardResult.lastInsertRowid, mowerId: batteryMowerId, date: new Date(today.getTime() - 30 * 24 * 60 * 60 * 1000), duration: 55, weather: 'Humid', notes: 'Very humid day, took frequent breaks' },
    { zoneId: sideGardenResult.lastInsertRowid, mowerId: electricMowerId, date: new Date(today.getTime() - 31 * 24 * 60 * 60 * 1000), duration: 28, weather: 'Sunny', notes: 'Beautiful day for yard work' },
    { zoneId: frontYardResult.lastInsertRowid, mowerId: batteryMowerId, date: new Date(today.getTime() - 35 * 24 * 60 * 60 * 1000), duration: 38, weather: 'Partly cloudy', notes: 'Standard weekly maintenance' },
    { zoneId: backYardResult.lastInsertRowid, mowerId: electricMowerId, date: new Date(today.getTime() - 44 * 24 * 60 * 60 * 1000), duration: 70, weather: 'Hot', notes: 'Very hot day, started early morning' },
  ];

  for (const history of sampleHistory) {
    await db.execute({
      sql: 'INSERT INTO mowing_history (zoneId, mowerId, mowedDate, duration, weather, notes) VALUES (?, ?, ?, ?, ?, ?)',
      args: [history.zoneId, history.mowerId, history.date.toISOString(), history.duration, history.weather, history.notes]
    });
  }
}

// Helper function to check if we're currently in mowing season
async function isInMowingSeason() {
  try {
    const result = await db.execute('SELECT * FROM season_settings WHERE id = 1');
    if (result.rows.length === 0) return true; // Default to always in season if no settings

    const settings = result.rows[0];
    if (!settings.isActive) return true; // If season is disabled, always in season

    const now = new Date();
    const currentMonth = now.getMonth() + 1;
    const currentDay = now.getDate();

    const startDate = new Date(now.getFullYear(), settings.startMonth - 1, settings.startDay);
    const endDate = new Date(now.getFullYear(), settings.endMonth - 1, settings.endDay);
    const currentDate = new Date(now.getFullYear(), currentMonth - 1, currentDay);

    if (startDate <= endDate) {
      // Same year season (e.g., May to September)
      return currentDate >= startDate && currentDate <= endDate;
    } else {
      // Cross-year season (e.g., November to March)
      return currentDate >= startDate || currentDate <= endDate;
    }
  } catch (error) {
    console.error('Error checking mowing season:', error);
    return true; // Default to in season on error
  }
}


// Helper function to calculate zone status
async function calculateZoneStatus(zone) {
  // First check if we're in mowing season
  const inSeason = await isInMowingSeason();
  if (!inSeason) {
    return {
      ...zone,
      daysSinceLastMow: null,
      daysUntilNext: null,
      status: 'off-season',
      isOverdue: false,
      isDueToday: false,
      isNew: false,
      isOffSeason: true
    };
  }

  const today = new Date();
  
  // For new zones that haven't been mowed yet
  if (!zone.lastMowedDate) {
    return {
      ...zone,
      daysSinceLastMow: null,
      daysUntilNext: null,
      status: 'new',
      isOverdue: false,
      isDueToday: false,
      isNew: true,
      isOffSeason: false
    };
  }
  
  const lastMowed = new Date(zone.lastMowedDate);
  const daysSinceLastMow = Math.floor((today - lastMowed) / (1000 * 60 * 60 * 24));
  
  let daysUntilNext;
  let status = 'ok';

  // Always check for nextMowDate first (this could be set by trimming or specific scheduling)
  if (zone.nextMowDate) {
    // Specific date scheduling
    const nextMowDate = new Date(zone.nextMowDate);
    daysUntilNext = Math.floor((nextMowDate - today) / (1000 * 60 * 60 * 24));
  } else if (zone.scheduleType === 'interval' && zone.intervalDays) {
    // Interval-based scheduling
    daysUntilNext = zone.intervalDays - daysSinceLastMow;
  } else {
    // No valid scheduling information
    daysUntilNext = null;
  }

  if (daysUntilNext !== null && daysUntilNext < 0) {
    status = 'overdue';
  } else if (daysUntilNext !== null && daysUntilNext <= 0) {
    status = 'due';
  } else if (daysUntilNext !== null && daysUntilNext <= 1) {
    status = 'due';
  }

  return {
    ...zone,
    daysSinceLastMow,
    daysUntilNext,
    status,
    isOverdue: daysUntilNext !== null && daysUntilNext < 0,
    isDueToday: daysUntilNext !== null && daysUntilNext <= 0 && daysUntilNext >= -1,
    isNew: false,
    isOffSeason: false
  };
}

// Helper function to calculate next mow date for interval scheduling
function calculateNextMowDate(lastMowedDate, intervalDays) {
  if (!lastMowedDate || !intervalDays) return null;
  const lastMowed = new Date(lastMowedDate);
  const nextMow = new Date(lastMowed.getTime() + (intervalDays * 24 * 60 * 60 * 1000));
  return nextMow.toISOString();
}

// Helper function to generate session ID
function generateSessionId() {
  return `session_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
}

// Mower routes
app.get('/api/mowers', async (req, res) => {
  try {
    const result = await db.execute('SELECT * FROM mowers WHERE isActive = 1 ORDER BY name');
    const mowers = result.rows.map(row => ({
      id: row.id,
      name: row.name,
      type: row.type,
      brand: row.brand,
      model: row.model,
      isActive: row.isActive,
      createdAt: row.createdAt
    }));
    res.json(mowers);
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

app.post('/api/mowers', async (req, res) => {
  try {
    const { name, type, brand, model } = req.body;
    
    const result = await db.execute({
      sql: 'INSERT INTO mowers (name, type, brand, model) VALUES (?, ?, ?, ?)',
      args: [name, type, brand || null, model || null]
    });
    
    const newMowerResult = await db.execute({
      sql: 'SELECT * FROM mowers WHERE id = ?',
      args: [result.lastInsertRowid]
    });
    
    const newMower = {
      id: newMowerResult.rows[0].id,
      name: newMowerResult.rows[0].name,
      type: newMowerResult.rows[0].type,
      brand: newMowerResult.rows[0].brand,
      model: newMowerResult.rows[0].model,
      isActive: newMowerResult.rows[0].isActive,
      createdAt: newMowerResult.rows[0].createdAt
    };
    
    res.status(201).json(newMower);
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

// Routes
app.get('/api/zones', async (req, res) => {
  try {
    const result = await db.execute('SELECT * FROM zones ORDER BY name');
    const zones = result.rows.map(row => ({
      id: row.id,
      name: row.name,
      imagePath: row.imagePath,
      lastMowedDate: row.lastMowedDate,
      intervalDays: row.intervalDays,
      nextMowDate: row.nextMowDate,
      scheduleType: row.scheduleType || 'interval',
      area: row.area || 0,
      createdAt: row.createdAt
    }));
    const zonesWithStatus = await Promise.all(zones.map(calculateZoneStatus));
    res.json(zonesWithStatus);
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

app.get('/api/zones/:id', async (req, res) => {
  try {
    const result = await db.execute({
      sql: 'SELECT * FROM zones WHERE id = ?',
      args: [req.params.id]
    });
    
    if (result.rows.length === 0) {
      return res.status(404).json({ error: 'Zone not found' });
    }
    
    const zone = {
      id: result.rows[0].id,
      name: result.rows[0].name,
      imagePath: result.rows[0].imagePath,
      lastMowedDate: result.rows[0].lastMowedDate,
      intervalDays: result.rows[0].intervalDays,
      nextMowDate: result.rows[0].nextMowDate,
      scheduleType: result.rows[0].scheduleType || 'interval',
      area: result.rows[0].area || 0,
      createdAt: result.rows[0].createdAt
    };
    
    const zoneWithStatus = calculateZoneStatus(zone);
    res.json(zoneWithStatus);
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

// Get mowing history for all zones or specific zone with pagination
app.get('/api/history', async (req, res) => {
  try {
    const { zoneId, limit = 10, offset = 0 } = req.query;
    
    // Get total count for pagination
    let countSql = `
      SELECT COUNT(*) as total 
      FROM mowing_history mh 
      JOIN zones z ON mh.zoneId = z.id
      LEFT JOIN mowers m ON mh.mowerId = m.id
    `;
    let countArgs = [];
    
    if (zoneId) {
      countSql += ' WHERE mh.zoneId = ?';
      countArgs.push(zoneId);
    }
    
    const countResult = await db.execute({ sql: countSql, args: countArgs });
    const total = countResult.rows[0].total;
    
    // Get paginated results
    let sql = `
      SELECT 
        mh.*,
        z.name as zoneName,
        z.area as zoneArea,
        m.name as mowerName,
        m.type as mowerType
      FROM mowing_history mh
      JOIN zones z ON mh.zoneId = z.id
      LEFT JOIN mowers m ON mh.mowerId = m.id
    `;
    let args = [];
    
    if (zoneId) {
      sql += ' WHERE mh.zoneId = ?';
      args.push(zoneId);
    }
    
    sql += ' ORDER BY mh.mowedDate DESC LIMIT ? OFFSET ?';
    args.push(parseInt(limit), parseInt(offset));
    
    const result = await db.execute({ sql, args });
    
    const history = result.rows.map(row => ({
      id: row.id,
      zoneId: row.zoneId,
      zoneName: row.zoneName,
      zoneArea: row.zoneArea,
      mowerId: row.mowerId,
      mowerName: row.mowerName,
      mowerType: row.mowerType,
      mowedDate: row.mowedDate,
      notes: row.notes,
      duration: row.duration,
      weather: row.weather,
      activityType: row.activityType || 'mowing',
      sessionId: row.sessionId,
      createdAt: row.createdAt
    }));
    
    // Calculate pagination info
    const currentPage = Math.floor(parseInt(offset) / parseInt(limit)) + 1;
    const totalPages = Math.ceil(total / parseInt(limit));
    const hasNextPage = currentPage < totalPages;
    const hasPrevPage = currentPage > 1;
    
    res.json({
      data: history,
      pagination: {
        total,
        currentPage,
        totalPages,
        hasNextPage,
        hasPrevPage,
        limit: parseInt(limit),
        offset: parseInt(offset)
      }
    });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

// Get mowing statistics
app.get('/api/history/stats', async (req, res) => {
  try {
    const { period = '30' } = req.query; // days
    const periodDays = parseInt(period);
    const startDate = new Date();
    startDate.setDate(startDate.getDate() - periodDays);
    
    // Total mowing sessions
    const totalResult = await db.execute({
      sql: `
        SELECT COUNT(*) as total FROM (
          SELECT 1 FROM mowing_history
          WHERE mowedDate >= ? AND sessionId IS NULL
          UNION ALL
          SELECT 1 FROM mowing_history
          WHERE mowedDate >= ? AND sessionId IS NOT NULL
          GROUP BY sessionId
        ) AS combined
      `,
      args: [startDate.toISOString(), startDate.toISOString()]
    });
    
    // Total time spent
    const timeResult = await db.execute({
      sql: 'SELECT SUM(duration) as totalMinutes FROM mowing_history WHERE mowedDate >= ? AND duration IS NOT NULL',
      args: [startDate.toISOString()]
    });
    
    // Total area mowed
    const areaResult = await db.execute({
      sql: `
        SELECT SUM(z.area) as totalArea 
        FROM mowing_history mh 
        JOIN zones z ON mh.zoneId = z.id 
        WHERE mh.mowedDate >= ?
      `,
      args: [startDate.toISOString()]
    });
    
    // Most active zone
    const activeZoneResult = await db.execute({
      sql: `
        SELECT z.name, COUNT(*) as sessions 
        FROM mowing_history mh 
        JOIN zones z ON mh.zoneId = z.id 
        WHERE mh.mowedDate >= ? 
        GROUP BY mh.zoneId, z.name 
        ORDER BY sessions DESC 
        LIMIT 1
      `,
      args: [startDate.toISOString()]
    });
    
    // Most used mower
    const activeMowerResult = await db.execute({
      sql: `
        SELECT m.name, m.type, COUNT(*) as sessions 
        FROM mowing_history mh 
        JOIN mowers m ON mh.mowerId = m.id 
        WHERE mh.mowedDate >= ? 
        GROUP BY mh.mowerId, m.name, m.type 
        ORDER BY sessions DESC 
        LIMIT 1
      `,
      args: [startDate.toISOString()]
    });
    
    // Daily activity for chart
    const dailyResult = await db.execute({
      sql: `
        SELECT 
          DATE(mowedDate) as date,
          COUNT(*) as sessions,
          SUM(duration) as totalDuration,
          SUM(z.area) as totalArea
        FROM mowing_history mh
        JOIN zones z ON mh.zoneId = z.id
        WHERE mowedDate >= ?
        GROUP BY DATE(mowedDate)
        ORDER BY date DESC
        LIMIT 30
      `,
      args: [startDate.toISOString()]
    });
    
    res.json({
      period: periodDays,
      totalSessions: totalResult.rows[0].total,
      totalMinutes: timeResult.rows[0].totalMinutes || 0,
      totalArea: areaResult.rows[0].totalArea || 0,
      mostActiveZone: activeZoneResult.rows[0] || null,
      mostUsedMower: activeMowerResult.rows[0] || null,
      dailyActivity: dailyResult.rows.map(row => ({
        date: row.date,
        sessions: row.sessions,
        duration: row.totalDuration || 0,
        area: row.totalArea || 0
      }))
    });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

app.post('/api/zones', upload.single('image'), async (req, res) => {
  try {
    const { name, intervalDays, nextMowDate, scheduleType, area } = req.body;
    const imagePath = req.file ? `/uploads/${req.file.filename}` : null;
    
    // For new zones, don't set lastMowedDate (they haven't been mowed yet)
    let sql, args;
    
    if (scheduleType === 'specific') {
      sql = 'INSERT INTO zones (name, imagePath, nextMowDate, scheduleType, area) VALUES (?, ?, ?, ?, ?)';
      args = [name, imagePath, nextMowDate, scheduleType, parseFloat(area) || 0];
    } else {
      sql = 'INSERT INTO zones (name, imagePath, intervalDays, scheduleType, area) VALUES (?, ?, ?, ?, ?)';
      args = [name, imagePath, parseInt(intervalDays), scheduleType || 'interval', parseFloat(area) || 0];
    }
    
    const result = await db.execute({ sql, args });
    
    const newZoneResult = await db.execute({
      sql: 'SELECT * FROM zones WHERE id = ?',
      args: [result.lastInsertRowid]
    });
    
    const newZone = {
      id: newZoneResult.rows[0].id,
      name: newZoneResult.rows[0].name,
      imagePath: newZoneResult.rows[0].imagePath,
      lastMowedDate: newZoneResult.rows[0].lastMowedDate,
      intervalDays: newZoneResult.rows[0].intervalDays,
      nextMowDate: newZoneResult.rows[0].nextMowDate,
      scheduleType: newZoneResult.rows[0].scheduleType || 'interval',
      area: newZoneResult.rows[0].area || 0,
      createdAt: newZoneResult.rows[0].createdAt
    };
    
    const zoneWithStatus = calculateZoneStatus(newZone);
    res.status(201).json(zoneWithStatus);
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

app.put('/api/zones/:id', upload.single('image'), async (req, res) => {
  try {
    const { name, intervalDays, lastMowedDate, nextMowDate, scheduleType, area } = req.body;
    
    const existingResult = await db.execute({
      sql: 'SELECT * FROM zones WHERE id = ?',
      args: [req.params.id]
    });
    
    if (existingResult.rows.length === 0) {
      return res.status(404).json({ error: 'Zone not found' });
    }
    
    const existingZone = existingResult.rows[0];
    const imagePath = req.file ? `/uploads/${req.file.filename}` : existingZone.imagePath;
    
    let sql, args;
    
    if (scheduleType === 'specific') {
      sql = 'UPDATE zones SET name = ?, imagePath = ?, lastMowedDate = ?, nextMowDate = ?, scheduleType = ?, intervalDays = NULL, area = ? WHERE id = ?';
      args = [name, imagePath, lastMowedDate || null, nextMowDate, scheduleType, parseFloat(area) || 0, req.params.id];
    } else {
      sql = 'UPDATE zones SET name = ?, imagePath = ?, lastMowedDate = ?, intervalDays = ?, scheduleType = ?, nextMowDate = NULL, area = ? WHERE id = ?';
      args = [name, imagePath, lastMowedDate || null, parseInt(intervalDays), scheduleType || 'interval', parseFloat(area) || 0, req.params.id];
    }

    console.log('Updating zone with data:', { name, intervalDays, lastMowedDate, nextMowDate, scheduleType, area });
    console.log('SQL:', sql);
    console.log('ARGS:', args);

    const updateResult = await db.execute({ sql, args });

    if (updateResult.rowsAffected === 0) {
      return res.status(500).json({ error: 'Failed to update zone' });
    }

    // Delete old image if new one was provided
    if (req.file && existingZone.imagePath) {
      const oldImagePath = path.join(process.cwd(), existingZone.imagePath.substring(1));
      if (fs.existsSync(oldImagePath)) {
        try {
          fs.unlinkSync(oldImagePath);
        } catch (error) {
          console.warn('Failed to delete old image:', error);
          // Don't fail the request if we can't delete the old image
        }
      }
    }

    const updatedResult = await db.execute({
      sql: 'SELECT * FROM zones WHERE id = ?',
      args: [req.params.id]
    });

    if (updatedResult.rows.length === 0) {
      return res.status(500).json({ error: 'Zone not found after update' });
    }

    const updatedZone = {
      id: updatedResult.rows[0].id,
      name: updatedResult.rows[0].name,
      imagePath: updatedResult.rows[0].imagePath,
      lastMowedDate: updatedResult.rows[0].lastMowedDate,
      intervalDays: updatedResult.rows[0].intervalDays,
      nextMowDate: updatedResult.rows[0].nextMowDate,
      scheduleType: updatedResult.rows[0].scheduleType || 'interval',
      area: updatedResult.rows[0].area || 0,
      createdAt: updatedResult.rows[0].createdAt
    };

    const zoneWithStatus = await calculateZoneStatus(updatedZone);
    res.json(zoneWithStatus);
  } catch (error) {
    console.error('Zone update error:', error);
    res.status(500).json({ error: error.message });
  }
});

app.delete('/api/zones/:id', async (req, res) => {
  try {
    const result = await db.execute({
      sql: 'SELECT * FROM zones WHERE id = ?',
      args: [req.params.id]
    });
    
    if (result.rows.length === 0) {
      return res.status(404).json({ error: 'Zone not found' });
    }
    
    const zone = result.rows[0];
    
    // Delete associated image
    if (zone.imagePath) {
      const imagePath = path.join(process.cwd(), zone.imagePath.substring(1));
      if (fs.existsSync(imagePath)) {
        fs.unlinkSync(imagePath);
      }
    }
    
    await db.execute({
      sql: 'DELETE FROM zones WHERE id = ?',
      args: [req.params.id]
    });
    
    res.status(204).send();
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

app.post('/api/zones/:id/mow', async (req, res) => {
  try {
    const { notes, duration, weather, mowerId } = req.body;
    const today = new Date().toISOString();
    
    // Get current zone data
    const zoneResult = await db.execute({
      sql: 'SELECT * FROM zones WHERE id = ?',
      args: [req.params.id]
    });
    
    if (zoneResult.rows.length === 0) {
      return res.status(404).json({ error: 'Zone not found' });
    }
    
    const zone = zoneResult.rows[0];
    
    // Update zone's last mowed date and calculate next mow date if using interval scheduling
    let sql, args;
    
    if (zone.scheduleType === 'interval' && zone.intervalDays) {
      const nextMowDate = calculateNextMowDate(today, zone.intervalDays);
      sql = 'UPDATE zones SET lastMowedDate = ?, nextMowDate = ? WHERE id = ?';
      args = [today, nextMowDate, req.params.id];
    } else {
      // For specific date scheduling, just update last mowed date
      sql = 'UPDATE zones SET lastMowedDate = ? WHERE id = ?';
      args = [today, req.params.id];
    }
    
    await db.execute({ sql, args });
    
    // Add to mowing history
    await db.execute({
      sql: 'INSERT INTO mowing_history (zoneId, mowerId, mowedDate, notes, duration, weather, activityType) VALUES (?, ?, ?, ?, ?, ?, ?)',
      args: [req.params.id, mowerId || null, today, notes || null, duration || null, weather || null, 'mowing']
    });
    
    const updatedResult = await db.execute({
      sql: 'SELECT * FROM zones WHERE id = ?',
      args: [req.params.id]
    });
    
    const updatedZone = {
      id: updatedResult.rows[0].id,
      name: updatedResult.rows[0].name,
      imagePath: updatedResult.rows[0].imagePath,
      lastMowedDate: updatedResult.rows[0].lastMowedDate,
      intervalDays: updatedResult.rows[0].intervalDays,
      nextMowDate: updatedResult.rows[0].nextMowDate,
      scheduleType: updatedResult.rows[0].scheduleType || 'interval',
      area: updatedResult.rows[0].area || 0,
      createdAt: updatedResult.rows[0].createdAt
    };

    const zoneWithStatus = await calculateZoneStatus(zone);
    res.json(zoneWithStatus);
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

// Trimming endpoint (update zone schedule by the week)
app.post('/api/zones/:id/trim', async (req, res) => {
  try {
    const { notes, duration, weather, mowerId } = req.body;
    const today = new Date().toISOString();
    
    // Get current zone data to verify it exists
    const zoneResult = await db.execute({
      sql: 'SELECT * FROM zones WHERE id = ?',
      args: [req.params.id]
    });
    
    if (zoneResult.rows.length === 0) {
      return res.status(404).json({ error: 'Zone not found' });
    }

    const zone = zoneResult.rows[0];

    // Update the zone's next mowing date by adding one week
    // This applies the rule that trimming delays mowing by a week
    let updatedNextMowDate = null;

    if (zone.scheduleType === 'interval' && zone.intervalDays) {
      // For interval scheduling, calculate new next mow date
      const baseDate = zone.lastMowedDate ? new Date(zone.lastMowedDate) : new Date();
      const originalNextMow = new Date(baseDate.getTime() + (zone.intervalDays * 24 * 60 * 60 * 1000));
      updatedNextMowDate = new Date(originalNextMow.getTime() + (7 * 24 * 60 * 60 * 1000)).toISOString();

      // Update the zone's nextMowDate
      await db.execute({
        sql: 'UPDATE zones SET nextMowDate = ? WHERE id = ?',
        args: [updatedNextMowDate, req.params.id]
      });
    } else if (zone.scheduleType === 'specific' && zone.nextMowDate) {
      // For specific date scheduling, push the date forward by one week
      const currentNextMow = new Date(zone.nextMowDate);
      updatedNextMowDate = new Date(currentNextMow.getTime() + (7 * 24 * 60 * 60 * 1000)).toISOString();

      // Update the zone's nextMowDate
      await db.execute({
        sql: 'UPDATE zones SET nextMowDate = ? WHERE id = ?',
        args: [updatedNextMowDate, req.params.id]
      });
    }

    // Add to mowing history with trimming activity type
    // Note: We don't update the zone's lastMowedDate or nextMowDate for trimming
    await db.execute({
      sql: 'INSERT INTO mowing_history (zoneId, mowerId, mowedDate, notes, duration, weather, activityType) VALUES (?, ?, ?, ?, ?, ?, ?)',
      args: [req.params.id, mowerId || null, today, notes || null, duration || null, weather || null, 'trimming']
    });

    res.json({
      success: true,
      message: 'Trimming recorded successfully. Next mowing date has been delayed by one week.',
      updatedNextMowDate
    });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

// Bulk mowing endpoint
app.post('/api/zones/bulk-mow', async (req, res) => {
  try {
    const { selectedZoneIds, notes, totalDuration, weather, mowerId, activityType = 'mowing' } = req.body;
    
    if (!selectedZoneIds || selectedZoneIds.length === 0) {
      return res.status(400).json({ error: 'No zones selected' });
    }
    
    const today = new Date().toISOString();
    const sessionId = generateSessionId();
    
    // Get zone data for selected zones
    const zoneResults = await Promise.all(
      selectedZoneIds.map(id => 
        db.execute({
          sql: 'SELECT * FROM zones WHERE id = ?',
          args: [id]
        })
      )
    );
    
    const zones = zoneResults.map(result => result.rows[0]).filter(Boolean);
    
    if (zones.length === 0) {
      return res.status(404).json({ error: 'No valid zones found' });
    }
    
    // Calculate total area for proportional time distribution
    const totalArea = zones.reduce((sum, zone) => sum + (zone.area || 0), 0);
    
    // Process each zone
    for (const zone of zones) {
      // Calculate proportional duration
      let zoneDuration = null;
      if (totalDuration && totalArea > 0 && zone.area > 0) {
        const proportion = zone.area / totalArea;
        zoneDuration = Math.round(totalDuration * proportion);
      }
      
      // Only update zone schedule for mowing, not trimming
      if (activityType === 'mowing') {
        // Update zone's last mowed date and calculate next mow date if using interval scheduling
        let sql, args;
        
        if (zone.scheduleType === 'interval' && zone.intervalDays) {
          const nextMowDate = calculateNextMowDate(today, zone.intervalDays);
          sql = 'UPDATE zones SET lastMowedDate = ?, nextMowDate = ? WHERE id = ?';
          args = [today, nextMowDate, zone.id];
        } else {
          // For specific date scheduling, just update last mowed date
          sql = 'UPDATE zones SET lastMowedDate = ? WHERE id = ?';
          args = [today, zone.id];
        }
        
        await db.execute({ sql, args });
      } else if (activityType === 'trimming') {
        // For trimming, delay the next mowing date by one week
        let updatedNextMowDate = null;

        if (zone.scheduleType === 'interval' && zone.intervalDays) {
          // For interval scheduling, calculate new next mow date
          const baseDate = zone.lastMowedDate ? new Date(zone.lastMowedDate) : new Date();
          const originalNextMow = new Date(baseDate.getTime() + (zone.intervalDays * 24 * 60 * 60 * 1000));
          updatedNextMowDate = new Date(originalNextMow.getTime() + (7 * 24 * 60 * 60 * 1000)).toISOString();

          // Update the zone's nextMowDate
          await db.execute({
            sql: 'UPDATE zones SET nextMowDate = ? WHERE id = ?',
            args: [updatedNextMowDate, zone.id]
          });
        } else if (zone.scheduleType === 'specific' && zone.nextMowDate) {
          // For specific date scheduling, push the date forward by one week
          const currentNextMow = new Date(zone.nextMowDate);
          updatedNextMowDate = new Date(currentNextMow.getTime() + (7 * 24 * 60 * 60 * 1000)).toISOString();

          // Update the zone's nextMowDate
          await db.execute({
            sql: 'UPDATE zones SET nextMowDate = ? WHERE id = ?',
            args: [updatedNextMowDate, zone.id]
          });
        }
      }
      
      // Add to mowing history with session ID
      await db.execute({
        sql: 'INSERT INTO mowing_history (zoneId, mowerId, mowedDate, notes, duration, weather, sessionId, activityType) VALUES (?, ?, ?, ?, ?, ?, ?, ?)',
        args: [zone.id, mowerId || null, today, notes || null, zoneDuration, weather || null, sessionId, activityType]
      });
    }
    
    res.json({ 
      success: true,
      message: `Successfully recorded ${activityType} session for ${zones.length} zones${activityType === 'trimming' ? '. Next mowing dates have been delayed by one week.' : ''}`,
      sessionId,
      zonesUpdated: zones.length
    });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

// Serve React app in production
if (process.env.NODE_ENV === 'production') {
  app.get('*', (req, res) => {
    res.sendFile(path.join(__dirname, '../public/index.html'));
  });
}

// Health check endpoint
app.get('/health', (req, res) => {
  res.status(200).json({ status: 'healthy', timestamp: new Date().toISOString() });
});

// Telegram test endpoint
app.post('/api/telegram/test', async (req, res) => {
  try {
    const result = await testTelegramConnection();
    res.json(result);
  } catch (error) {
    res.status(500).json({ success: false, message: error.message });
  }
});

// Manual trigger for mowing reminders
app.post('/api/telegram/check-reminders', async (req, res) => {
  try {
    await checkMowingReminders();
    res.json({ success: true, message: 'Mowing reminders checked and sent if needed' });
  } catch (error) {
    res.status(500).json({ success: false, message: error.message });
  }
});

// Manual trigger for weekly report
app.post('/api/telegram/weekly-report', async (req, res) => {
  try {
    await sendWeeklyReport();
    res.json({ success: true, message: 'Weekly report sent successfully' });
  } catch (error) {
    res.status(500).json({ success: false, message: error.message });
  }
});

// Season settings routes
app.get('/api/season', async (req, res) => {
  try {
    const result = await db.execute('SELECT * FROM season_settings WHERE id = 1');
    if (result.rows.length === 0) {
      // Return default settings if none exist
      res.json({
        startMonth: 5,
        startDay: 1,
        endMonth: 9,
        endDay: 30,
        isActive: true
      });
    } else {
      const settings = result.rows[0];
      res.json({
        id: settings.id,
        startMonth: settings.startMonth,
        startDay: settings.startDay,
        endMonth: settings.endMonth,
        endDay: settings.endDay,
        isActive: settings.isActive,
        createdAt: settings.createdAt,
        updatedAt: settings.updatedAt
      });
    }
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

app.put('/api/season', async (req, res) => {
  try {
    const { startMonth, startDay, endMonth, endDay, isActive } = req.body;

    // Check if settings exist
    const existingResult = await db.execute('SELECT * FROM season_settings WHERE id = 1');

    if (existingResult.rows.length === 0) {
      // Create new settings
      const result = await db.execute({
        sql: 'INSERT INTO season_settings (startMonth, startDay, endMonth, endDay, isActive) VALUES (?, ?, ?, ?, ?)',
        args: [startMonth, startDay, endMonth, endDay, isActive]
      });

      const newSettings = await db.execute({
        sql: 'SELECT * FROM season_settings WHERE id = ?',
        args: [result.lastInsertRowid]
      });

      res.json({
        id: newSettings.rows[0].id,
        startMonth: newSettings.rows[0].startMonth,
        startDay: newSettings.rows[0].startDay,
        endMonth: newSettings.rows[0].endMonth,
        endDay: newSettings.rows[0].endDay,
        isActive: newSettings.rows[0].isActive,
        createdAt: newSettings.rows[0].createdAt,
        updatedAt: newSettings.rows[0].updatedAt
      });
    } else {
      // Update existing settings
      await db.execute({
        sql: 'UPDATE season_settings SET startMonth = ?, startDay = ?, endMonth = ?, endDay = ?, isActive = ?, updatedAt = CURRENT_TIMESTAMP WHERE id = 1',
        args: [startMonth, startDay, endMonth, endDay, isActive]
      });

      const updatedSettings = await db.execute('SELECT * FROM season_settings WHERE id = 1');

      res.json({
        id: updatedSettings.rows[0].id,
        startMonth: updatedSettings.rows[0].startMonth,
        startDay: updatedSettings.rows[0].startDay,
        endMonth: updatedSettings.rows[0].endMonth,
        endDay: updatedSettings.rows[0].endDay,
        isActive: updatedSettings.rows[0].isActive,
        createdAt: updatedSettings.rows[0].createdAt,
        updatedAt: updatedSettings.rows[0].updatedAt
      });
    }
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

app.listen(PORT, '0.0.0.0', () => {
  console.log(`Server running on http://0.0.0.0:${PORT}`);
  console.log(`Environment: ${process.env.NODE_ENV || 'development'}`);

  // Setup cron jobs for Telegram notifications
  if (process.env.TELEGRAM_BOT_TOKEN && process.env.TELEGRAM_CHAT_ID) {
    console.log('Setting up Telegram notifications...');

    // Daily mowing reminders at 8:00 AM
    cron.schedule('0 8 * * *', () => {
      console.log('Running daily mowing reminder check...');
      checkMowingReminders();
    }, {
      timezone: 'Europe/Moscow' // Adjust timezone as needed
    });

    // Weekly report every Sunday at 9:00 AM
    cron.schedule('0 9 * * 0', () => {
      console.log('Sending weekly lawn care report...');
      sendWeeklyReport();
    }, {
      timezone: 'Europe/Moscow' // Adjust timezone as needed
    });

    console.log('Telegram notifications scheduled successfully');
  } else {
    console.log('Telegram notifications not configured (missing TELEGRAM_BOT_TOKEN or TELEGRAM_CHAT_ID)');
  }
});