import api from '../store/api';
import { query, execute } from './db';
import { useAuthStore } from '../store/auth';

class DeviceSyncEngine {
  private syncInterval: number = 30000; // 30 seconds
  private isRunning: boolean = false;
  private timer: any = null;

  constructor() {}

  async start() {
    if (this.timer) return;
    
    console.log('🔄 Device Sync Engine started');
    
    // Initial sync after short delay to ensure auth is ready
    setTimeout(() => this.sync().catch(console.error), 2000);

    this.timer = setInterval(() => {
      this.sync().catch(err => console.error('Sync failed', err));
    }, this.syncInterval);
  }

  async stop() {
    if (this.timer) {
      clearInterval(this.timer);
      this.timer = null;
    }
  }

  async sync() {
    if (this.isRunning) return;
    const { token } = useAuthStore.getState();
    if (!token) return; // Don't sync if not logged in

    this.isRunning = true;

    try {
      // 1. Push local changes
      await this.pushLocalChanges();

      // 2. Pull remote changes
      await this.pullRemoteChanges();

    } catch (err) {
      console.error('Device Sync Error:', err);
    } finally {
      this.isRunning = false;
    }
  }

  private async pushLocalChanges() {
    // Sync all client-writable tables
    const tables = [
      'users', 'attendance', 'grades', 'submissions',
      'payments', 'messages', 'events', 'parent_students',
      'student_medical_profiles', 'student_medication_logs', 'student_medical_history'
    ];
    const payload: any = {};

    for (const table of tables) {
      try {
        const pending = await query(`SELECT * FROM ${table} WHERE sync_status = 'pending'`);
        if (pending && pending.length > 0) {
          payload[table] = pending;
        }
      } catch (e) { /* Table might not exist yet */ }
    }

    if (Object.keys(payload).length === 0) return;

    console.log('📤 Pushing device changes to hub...', payload);

    try {
      const response = await api.post('/sync/push', { changes: payload });
      if (response.data.success) {
        // Mark local as synced
        for (const [table, records] of Object.entries(payload)) {
          const uids = (records as any[]).map(r => `'${r.uid}'`).join(',');
          await execute(`UPDATE ${table} SET sync_status = 'synced' WHERE uid IN (${uids})`);
        }
        console.log('✅ Local changes pushed successfully');
      }
    } catch (err) {
      console.warn('Failed to push changes (Hub might be offline)');
    }
  }

  private async pullRemoteChanges() {
    let lastSync = '1970-01-01T00:00:00Z';
    try {
      const config = await query("SELECT value FROM sync_config WHERE key = 'last_sync'");
      if (config && config[0]?.value) {
        lastSync = config[0].value;
      }
    } catch (e) { /* Table config might not exist */ }

    try {
      const response = await api.get('/sync/pull', { params: { last_sync: lastSync } });
      if (response.data.success) {
        const { changes, server_time } = response.data;

        for (const [table, records] of Object.entries(changes)) {
          for (const record of (records as any[])) {
            await this.upsertRecord(table, record);
          }
        }

        await execute("INSERT OR REPLACE INTO sync_config (key, value) VALUES ('last_sync', ?)", [server_time]);
        if (Object.keys(changes).length > 0) {
          console.log(`📥 Pulled ${Object.keys(changes).length} tables from hub`);
          window.dispatchEvent(new CustomEvent('sync_complete'));
        }
      }
    } catch (err) {
      console.warn('Failed to pull changes (Hub might be offline)');
    }
  }

  private async upsertRecord(tableName: string, record: any) {
    try {
      const columns = Object.keys(record).filter(k => k !== 'id');
      const placeholders = columns.map(() => '?').join(', ');
      const updateList = columns.filter(k => k !== 'uid').map(k => `${k} = EXCLUDED.${k}`).join(', ');

      await execute(`
        INSERT INTO ${tableName} (${columns.join(', ')})
        VALUES (${placeholders})
        ON CONFLICT(uid) DO UPDATE SET ${updateList}, sync_status = 'synced'
      `, columns.map(k => record[k]));
    } catch (e: any) {
      if (e?.message?.includes('no such table')) {
        // Safe to ignore: table is not defined in the local client database
        console.debug(`Skipped upsert for table '${tableName}': table does not exist on this client.`);
      } else {
        console.error(`Failed to upsert ${tableName}`, e);
      }
    }
  }
}

export const syncEngine = new DeviceSyncEngine();
