import { Injectable, Logger, Inject } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { ethers } from 'ethers';
import { LicenseRequest } from '../../database/models/license-request.model';
import { Applicant } from '../../database/models/applicant.model';
import { Department } from '../../database/models/department.model';
import { User } from '../../database/models/user.model';
import { Document } from '../../database/models/document.model';
import { BlockchainTransaction } from '../../database/models/blockchain-transaction.model';
import { BlockchainEvent } from '../../database/models/blockchain-event.model';
import { ApplicationLog } from '../../database/models/application-log.model';
import { AuditLog } from '../../database/models/audit-log.model';
import { DepartmentsService } from '../departments/departments.service';
import { UsersService } from '../users/users.service';

export interface StatusCount {
  status: string;
  count: number;
}

export interface PlatformStats {
  totalRequests: number;
  totalApprovals: number;
  requestsByStatus: StatusCount[];
  totalApplicants: number;
  activeApplicants: number;
  totalDepartments: number;
  activeDepartments: number;
  totalDocuments: number;
  totalBlockchainTransactions: number;
  transactionsByStatus: StatusCount[];
  averageProcessingTime: number;
  lastUpdated: string;
}

export interface SystemHealth {
  status: 'healthy' | 'degraded' | 'unhealthy';
  uptime: number;
  timestamp: string;
  services: {
    database: { status: string };
    blockchain: { status: string };
    storage: { status: string };
    queue: { status: string };
  };
}

@Injectable()
export class AdminService {
  private readonly logger = new Logger(AdminService.name);

  constructor(
    @Inject(LicenseRequest) private requestModel: typeof LicenseRequest,
    @Inject(Applicant) private applicantModel: typeof Applicant,
    @Inject(Department) private departmentModel: typeof Department,
    @Inject(User) private userModel: typeof User,
    @Inject(Document) private documentModel: typeof Document,
    @Inject(BlockchainTransaction) private blockchainTxModel: typeof BlockchainTransaction,
    @Inject(BlockchainEvent) private blockchainEventModel: typeof BlockchainEvent,
    @Inject(ApplicationLog) private appLogModel: typeof ApplicationLog,
    @Inject(AuditLog) private auditLogModel: typeof AuditLog,
    private readonly configService: ConfigService,
    private readonly departmentsService: DepartmentsService,
    private readonly usersService: UsersService,
  ) {}

  async getPlatformStats(): Promise<PlatformStats> {
    this.logger.debug('Fetching platform statistics');

    const [
      totalRequests,
      requestsByStatus,
      totalApplicants,
      activeApplicants,
      totalDepartments,
      activeDepartments,
      totalDocuments,
      totalBlockchainTransactions,
      transactionsByStatus,
    ] = await Promise.all([
      this.requestModel.query().resultSize(),
      this.requestModel.query().select('status').count('* as count').groupBy('status') as any,
      this.applicantModel.query().resultSize(),
      this.applicantModel.query().where({ is_active: true }).resultSize(),
      this.departmentModel.query().resultSize(),
      this.departmentModel.query().where({ is_active: true }).resultSize(),
      this.documentModel.query().resultSize(),
      this.blockchainTxModel.query().resultSize(),
      this.blockchainTxModel.query().select('status').count('* as count').groupBy('status') as any,
    ]);

    // Convert to array format expected by frontend
    const statusArray: StatusCount[] = requestsByStatus.map((row: any) => ({
      status: row.status,
      count: parseInt(row.count, 10),
    }));

    const txStatusArray: StatusCount[] = transactionsByStatus.map((row: any) => ({
      status: row.status,
      count: parseInt(row.count, 10),
    }));

    // Calculate total approvals
    const approvedCount = statusArray.find(s => s.status === 'APPROVED')?.count || 0;

    return {
      totalRequests,
      totalApprovals: approvedCount,
      requestsByStatus: statusArray,
      totalApplicants,
      activeApplicants,
      totalDepartments,
      activeDepartments,
      totalDocuments,
      totalBlockchainTransactions,
      transactionsByStatus: txStatusArray,
      averageProcessingTime: 4.5, // Placeholder
      lastUpdated: new Date().toISOString(),
    };
  }

  async getSystemHealth(): Promise<SystemHealth> {
    this.logger.debug('Checking system health');

    let dbStatus = 'up';
    try {
      await this.applicantModel.query().limit(1);
    } catch {
      dbStatus = 'down';
    }

    const overallStatus = dbStatus === 'up' ? 'healthy' : 'unhealthy';

    return {
      status: overallStatus,
      uptime: process.uptime(),
      timestamp: new Date().toISOString(),
      services: {
        database: { status: dbStatus },
        blockchain: { status: 'up' },
        storage: { status: 'up' },
        queue: { status: 'up' },
      },
    };
  }

  async getRecentActivity(limit: number = 20): Promise<AuditLog[]> {
    return this.auditLogModel.query().orderBy('created_at', 'DESC').limit(limit);
  }

  async getBlockchainTransactions(page: number = 1, limit: number = 20, status?: string) {
    const query = this.blockchainTxModel.query().orderBy('created_at', 'DESC');

    if (status) {
      query.where({ status });
    }

    const offset = (page - 1) * limit;
    const [results, total] = await Promise.all([
      query.clone().offset(offset).limit(limit),
      query.clone().resultSize(),
    ]);

    return {
      data: results,
      total,
      page,
      limit,
      totalPages: Math.ceil(total / limit),
    };
  }

  async onboardDepartment(dto: any) {
    this.logger.debug(`Onboarding new department: ${dto.code}`);
    const result = await this.departmentsService.create(dto);
    return {
      department: result.department,
      apiKey: result.apiKey,
      apiSecret: result.apiSecret,
      message:
        'Department onboarded successfully. Please save the API credentials as they will not be shown again.',
    };
  }

  async getDepartments(page: number = 1, limit: number = 20) {
    return this.departmentsService.findAll({ page, limit });
  }

  async getDepartment(id: string) {
    return this.departmentsService.findById(id);
  }

  async updateDepartment(id: string, dto: any) {
    return this.departmentsService.update(id, dto);
  }

  async regenerateDepartmentApiKey(id: string) {
    const result = await this.departmentsService.regenerateApiKey(id);
    return {
      ...result,
      message:
        'API key regenerated successfully. Please save the new credentials as they will not be shown again.',
    };
  }

  async deactivateDepartment(id: string) {
    await this.departmentsService.deactivate(id);
    return { message: 'Department deactivated successfully' };
  }

  async activateDepartment(id: string) {
    const department = await this.departmentsService.activate(id);
    return { department, message: 'Department activated successfully' };
  }

  async getUsers() {
    return this.usersService.findAll();
  }

  async getBlockchainEvents(
    page: number = 1,
    limit: number = 20,
    eventType?: string,
    contractAddress?: string,
  ) {
    const query = this.blockchainEventModel.query().orderBy('created_at', 'DESC');

    if (eventType) {
      query.where({ eventType });
    }

    if (contractAddress) {
      query.where({ contractAddress });
    }

    const offset = (page - 1) * limit;
    const [results, total] = await Promise.all([
      query.clone().offset(offset).limit(limit),
      query.clone().resultSize(),
    ]);

    return {
      data: results,
      total,
      page,
      limit,
      totalPages: Math.ceil(total / limit),
    };
  }

  async getApplicationLogs(
    page: number = 1,
    limit: number = 50,
    level?: string,
    module?: string,
    search?: string,
  ) {
    const query = this.appLogModel.query().orderBy('created_at', 'DESC');

    if (level) {
      query.where({ level });
    }

    if (module) {
      query.where('module', 'like', `%${module}%`);
    }

    if (search) {
      query.where('message', 'like', `%${search}%`);
    }

    const offset = (page - 1) * limit;
    const [results, total] = await Promise.all([
      query.clone().offset(offset).limit(limit),
      query.clone().resultSize(),
    ]);

    return {
      data: results,
      total,
      page,
      limit,
      totalPages: Math.ceil(total / limit),
    };
  }

  async getRequestDocuments(requestId: string) {
    this.logger.debug(`Fetching documents for request: ${requestId}`);

    // Fetch all documents for the request with related data
    const documents = await this.documentModel
      .query()
      .where({ requestId })
      .withGraphFetched(
        '[uploadedByUser, versions.[uploadedByUser], departmentReviews.[department]]',
      )
      .orderBy('created_at', 'DESC');

    // Transform documents to include formatted data
    return documents.map((doc: any) => ({
      id: doc.id,
      name: doc.name,
      type: doc.type,
      size: doc.size,
      fileHash: doc.fileHash,
      ipfsHash: doc.ipfsHash,
      url: doc.url,
      thumbnailUrl: doc.thumbnailUrl,
      uploadedAt: doc.createdAt,
      uploadedBy: doc.uploadedByUser?.name || 'Unknown',
      currentVersion: doc.version || 1,
      versions:
        doc.versions?.map((v: any) => ({
          id: v.id,
          version: v.version,
          fileHash: v.fileHash,
          uploadedAt: v.createdAt,
          uploadedBy: v.uploadedByUser?.name || 'Unknown',
          changes: v.changes,
        })) || [],
      departmentReviews:
        doc.departmentReviews?.map((review: any) => ({
          departmentCode: review.department?.code || 'UNKNOWN',
          departmentName: review.department?.name || 'Unknown Department',
          reviewedAt: review.createdAt,
          reviewedBy: review.reviewedByUser?.name || 'Unknown',
          status: review.status,
          comments: review.comments,
        })) || [],
      metadata: {
        mimeType: doc.mimeType,
        width: doc.width,
        height: doc.height,
        pages: doc.pages,
      },
    }));
  }

  async getBlockchainBlocks(limit: number = 5) {
    this.logger.debug(`Fetching ${limit} recent blockchain blocks`);

    try {
      const rpcUrl = this.configService.get<string>('BESU_RPC_URL') || 'http://besu-node-1:8545';
      const provider = new ethers.JsonRpcProvider(rpcUrl);

      const latestBlockNumber = await provider.getBlockNumber();
      const blocks = [];

      for (let i = 0; i < limit && latestBlockNumber - i >= 0; i++) {
        const blockNumber = latestBlockNumber - i;
        const block = await provider.getBlock(blockNumber);

        if (block) {
          blocks.push({
            blockNumber: block.number,
            hash: block.hash,
            parentHash: block.parentHash,
            timestamp: new Date(block.timestamp * 1000).toISOString(),
            transactionCount: block.transactions.length,
            gasUsed: Number(block.gasUsed),
            gasLimit: Number(block.gasLimit),
            miner: block.miner,
            nonce: block.nonce,
          });
        }
      }

      return { data: blocks };
    } catch (error) {
      this.logger.error('Failed to fetch blockchain blocks', error);
      // Return empty array on error - frontend will use mock data
      return { data: [] };
    }
  }
}
