/**
 * 本地数据库数据源实现
 * 封装所有 SQLite 操作，提供统一的错误处理和事务支持
 */

import * as SQLite from 'expo-sqlite';
import { ILocalDataSource, DataSourceError } from './interfaces';

// 数据库实例管理
let dbInstance: SQLite.SQLiteDatabase | null = null;
let currentDbName: string | null = null;
let writeQueue: Promise<void> = Promise.resolve();

export interface LocalDataSourceConfig {
  dbName?: string;
  userId?: string;
}

export class LocalDataSource implements ILocalDataSource {
  private db: SQLite.SQLiteDatabase | null = null;
  private dbName: string;
  private initialized = false;
  private initializingPromise: Promise<void> | null = null;

  constructor(config: LocalDataSourceConfig = {}) {
    // 如果提供了userId，使用用户专属数据库
    this.dbName = config.dbName || (config.userId ? `carrot_bbs_${config.userId}.db` : 'carrot_bbs.db');
  }

  /**
   * 初始化数据库连接
   */
  async initialize(): Promise<void> {
    if (this.initialized && this.db) {
      return;
    }

    // 防止并发初始化导致连接状态竞争
    if (this.initializingPromise) {
      await this.initializingPromise;
      return;
    }

    this.initializingPromise = this.doInitialize();
    try {
      await this.initializingPromise;
    } finally {
      this.initializingPromise = null;
    }
  }

  private async doInitialize(): Promise<void> {
    try {
      // 使用全局实例管理
      if (dbInstance && currentDbName === this.dbName) {
        this.db = dbInstance;
        this.initialized = true;
        return;
      }

      // 关闭旧连接
      if (dbInstance) {
        try {
          await dbInstance.closeAsync();
        } catch (e) {
          console.warn('关闭旧数据库连接失败:', e);
        }
      }

      // 创建新连接
      this.db = await SQLite.openDatabaseAsync(this.dbName);
      dbInstance = this.db;
      currentDbName = this.dbName;
      this.initialized = true;

      // 初始化数据库表结构
      await this.createTables();
    } catch (error) {
      this.handleError(error, 'INITIALIZE');
    }
  }

  /**
   * 创建数据库表结构
   */
  private async createTables(): Promise<void> {
    if (!this.db) return;

    const tables = [
      // 消息表
      `CREATE TABLE IF NOT EXISTS messages (
        id TEXT PRIMARY KEY NOT NULL,
        conversationId TEXT NOT NULL,
        senderId TEXT NOT NULL,
        content TEXT,
        type TEXT DEFAULT 'text',
        isRead INTEGER DEFAULT 0,
        createdAt TEXT NOT NULL,
        seq INTEGER DEFAULT 0,
        status TEXT DEFAULT 'normal',
        segments TEXT
      )`,
      // 会话表
      `CREATE TABLE IF NOT EXISTS conversations (
        id TEXT PRIMARY KEY NOT NULL,
        participantId TEXT NOT NULL,
        lastMessageId TEXT,
        lastSeq INTEGER DEFAULT 0,
        unreadCount INTEGER DEFAULT 0,
        createdAt TEXT NOT NULL,
        updatedAt TEXT NOT NULL
      )`,
      // 会话缓存表
      `CREATE TABLE IF NOT EXISTS conversation_cache (
        id TEXT PRIMARY KEY NOT NULL,
        data TEXT NOT NULL,
        updatedAt TEXT NOT NULL
      )`,
      // 会话列表缓存表
      `CREATE TABLE IF NOT EXISTS conversation_list_cache (
        id TEXT PRIMARY KEY NOT NULL,
        data TEXT NOT NULL,
        updatedAt TEXT NOT NULL
      )`,
      // 用户缓存表
      `CREATE TABLE IF NOT EXISTS users (
        id TEXT PRIMARY KEY NOT NULL,
        data TEXT NOT NULL,
        updatedAt TEXT NOT NULL
      )`,
      // 当前登录用户缓存
      `CREATE TABLE IF NOT EXISTS current_user_cache (
        id TEXT PRIMARY KEY NOT NULL,
        data TEXT NOT NULL,
        updatedAt TEXT NOT NULL
      )`,
      // 群组缓存表
      `CREATE TABLE IF NOT EXISTS groups (
        id TEXT PRIMARY KEY NOT NULL,
        data TEXT NOT NULL,
        updatedAt TEXT NOT NULL
      )`,
      // 群成员缓存表
      `CREATE TABLE IF NOT EXISTS group_members (
        groupId TEXT NOT NULL,
        userId TEXT NOT NULL,
        data TEXT NOT NULL,
        updatedAt TEXT NOT NULL,
        PRIMARY KEY (groupId, userId)
      )`,
      // 帖子缓存表
      `CREATE TABLE IF NOT EXISTS posts_cache (
        id TEXT PRIMARY KEY NOT NULL,
        data TEXT NOT NULL,
        updatedAt TEXT NOT NULL
      )`,
    ];

    for (const sql of tables) {
      await this.db.execAsync(sql);
    }

    // 创建索引
    const indexes = [
      `CREATE INDEX IF NOT EXISTS idx_messages_conversationId ON messages(conversationId)`,
      `CREATE INDEX IF NOT EXISTS idx_messages_seq ON messages(seq)`,
      `CREATE INDEX IF NOT EXISTS idx_messages_conversation_seq ON messages(conversationId, seq)`,
      `CREATE INDEX IF NOT EXISTS idx_group_members_groupId ON group_members(groupId)`,
    ];

    for (const sql of indexes) {
      await this.db.execAsync(sql);
    }
  }

  /**
   * 处理错误
   */
  private handleError(error: unknown, operation: string): never {
    const message = error instanceof Error ? error.message : 'Unknown error';
    throw new DataSourceError(
      `Database ${operation} failed: ${message}`,
      'DB_ERROR',
      'LocalDataSource',
      error instanceof Error ? error : undefined
    );
  }

  /**
   * 检查数据库连接
   */
  private ensureDb(): SQLite.SQLiteDatabase {
    if (!this.db) {
      throw new DataSourceError(
        'Database not initialized',
        'DB_NOT_INITIALIZED',
        'LocalDataSource'
      );
    }
    return this.db;
  }

  // ==================== ILocalDataSource 实现 ====================

  async query<T>(sql: string, params?: any[]): Promise<T[]> {
    try {
      const db = this.ensureDb();
      if (params && params.length > 0) {
        return await db.getAllAsync<T>(sql, params as any);
      }
      return await db.getAllAsync<T>(sql);
    } catch (error) {
      this.handleError(error, 'QUERY');
    }
  }

  async getFirst<T>(sql: string, params?: any[]): Promise<T | null> {
    try {
      const db = this.ensureDb();
      if (params && params.length > 0) {
        return await db.getFirstAsync<T>(sql, params as any);
      }
      return await db.getFirstAsync<T>(sql);
    } catch (error) {
      this.handleError(error, 'GET_FIRST');
    }
  }

  async execute(sql: string, params?: any[]): Promise<void> {
    try {
      const db = this.ensureDb();
      await db.execAsync(sql);
    } catch (error) {
      this.handleError(error, 'EXECUTE');
    }
  }

  async run(sql: string, params?: any[]): Promise<{ lastInsertRowId: number; changes: number }> {
    try {
      const db = this.ensureDb();
      if (params && params.length > 0) {
        return await db.runAsync(sql, params as any);
      }
      return await db.runAsync(sql);
    } catch (error) {
      if (this.isRecoverableError(error)) {
        this.db = null;
        this.initialized = false;
        dbInstance = null;
        await this.initialize();
        const db = this.ensureDb();
        if (params && params.length > 0) {
          return await db.runAsync(sql, params as any);
        }
        return await db.runAsync(sql);
      }
      this.handleError(error, 'RUN');
    }
  }

  async transaction(operations: () => Promise<void>): Promise<void> {
    try {
      const db = this.ensureDb();
      await db.withTransactionAsync(async () => {
        await operations();
      });
    } catch (error) {
      this.handleError(error, 'TRANSACTION');
    }
  }

  async batch(operations: (db: ILocalDataSource) => Promise<void>): Promise<void> {
    try {
      await this.transaction(async () => {
        await operations(this);
      });
    } catch (error) {
      this.handleError(error, 'BATCH');
    }
  }

  // ==================== 队列写入支持 ====================

  /**
   * 使用队列执行写入操作（避免并发写入冲突）
   */
  async enqueueWrite<T>(operation: () => Promise<T>): Promise<T> {
    const wrappedOperation = async () => {
      try {
        return await operation();
      } catch (error) {
        // 尝试重连后重试
        if (this.isRecoverableError(error)) {
          console.error('数据库写入异常，尝试重连后重试:', error);
          this.db = null;
          dbInstance = null;
          await this.initialize();
          return operation();
        }
        throw error;
      }
    };

    const queued = writeQueue.then(wrappedOperation, wrappedOperation);
    writeQueue = queued.then(() => undefined, () => undefined);
    return queued;
  }

  /**
   * 判断错误是否可恢复
   */
  private isRecoverableError(error: unknown): boolean {
    const message = String(error);
    return (
      message.includes('NativeDatabase.prepareAsync') ||
      message.includes('NullPointerException') ||
      message.includes('database is locked')
    );
  }
}

// 导出单例实例
export const localDataSource = new LocalDataSource();
