import { Injectable, HttpException, HttpStatus } from '@nestjs/common';
import { ModelsService } from 'src/models/models.service';
import { CommonService } from 'src/common/common.service';
import { Types } from 'mongoose';
import { CreateLanguageDto } from './dto/create-language.dto';
import { UpdateLanguageDto } from './dto/update-language.dto';
import { UpdateTranslationDto } from './dto/update-translation.dto';
import { CreateTranslationKeyDto } from './dto/create-translation-key.dto';
import { UpdateTranslationKeyDto } from './dto/update-translation-key.dto';
import { parse } from 'json2csv';
import { Readable } from 'stream';
const csv = require('csv-parser');

@Injectable()
export class LanguagesService {
  constructor(
    private readonly models: ModelsService,
    private readonly commonService: CommonService,
  ) { }

  async create(createLanguageDto: CreateLanguageDto) {
    const existing = await this.models.LanguageModel.findOne({
      code: createLanguageDto.code
    });

    if (existing) {
      throw new HttpException(
        'Language code already exists',
        HttpStatus.BAD_REQUEST,
      );
    }

    if (createLanguageDto.is_default) {
      await this.models.LanguageModel.updateMany(
        {},
        { $set: { is_default: false } }
      );
    }

    const language = new this.models.LanguageModel({
      ...createLanguageDto,
      created_at: +new Date(),
      updated_at: +new Date()
    });

    return await language.save();
  }

  async findAll(page: number = 1, limit: number = 10, search?: string) {
    const query: any = {};
    
    if (search) {
      query.name = { $regex: search, $options: 'i' };
    }

    const skip = (page - 1) * limit;

    const [data, total] = await Promise.all([
      this.models.LanguageModel.find(query)
        .sort({ created_at: -1 })
        .skip(skip)
        .limit(limit)
        .lean(),
      this.models.LanguageModel.countDocuments(query)
    ]);

    return this.commonService.paginatedResponse(
      'Languages retrieved successfully',
      data,
      total,
      page,
      limit
    );
  }

  async findOne(id: string) {
    const language = await this.models.LanguageModel.findOne({
      _id: new Types.ObjectId(id)
    }).lean();

    if (!language) {
      throw new HttpException('Language not found', HttpStatus.NOT_FOUND);
    }
    return this.commonService.successResponse('Language retrieved successfully', language);
  }

  async update(id: string, updateLanguageDto: UpdateLanguageDto) {
    const language = await this.findOne(id);

    if (updateLanguageDto.is_default) {
      await this.models.LanguageModel.updateMany(
        {},
        { $set: { is_default: false } }
      );
    }

    const updated = await this.models.LanguageModel.findOneAndUpdate(
      { _id: new Types.ObjectId(id) },
      { $set: { ...updateLanguageDto, updated_at: +new Date() } },
      { new: true }
    );

    return updated;
  }

  async remove(id: string) {
    const {data : language} = await this.findOne(id);
    
    if (language.is_default) {
      throw new HttpException(
        'Cannot delete default language',
        HttpStatus.BAD_REQUEST,
      );
    }

    await this.models.LanguageModel.deleteOne({
      _id: new Types.ObjectId(id)
    });

    return { message: 'Language deleted successfully' };
  }

  async createTranslationKey(createTranslationKeyDto: CreateTranslationKeyDto) {
    const { key, default_text, language_code, translation_value } = createTranslationKeyDto;

    // Check if key already exists
    const existing = await this.models.TranslationKeyModel.findOne({ key });
    if (existing) {
      throw new HttpException(
        'Translation key already exists',
        HttpStatus.BAD_REQUEST,
      );
    }

    // Create translation key
    const translationKey = new this.models.TranslationKeyModel({
      key,
      default_text,
      created_at: +new Date(),
      updated_at: +new Date()
    });

    const savedKey = await translationKey.save();

    // If language_code and translation_value provided, create translation
    if (language_code && translation_value) {
      const language = await this.models.LanguageModel.findOne({ code: language_code });
      if (language) {
        const translation = new this.models.TranslationModel({
          language_id: language._id,
          key_id: savedKey._id,
          value: translation_value,
          created_at: +new Date(),
          updated_at: +new Date()
        });
        await translation.save();
      }
    }

    return savedKey;
  }

  async updateTranslationKey(id: string, updateTranslationKeyDto: UpdateTranslationKeyDto) {
    const { key, default_text, language_code, translation_value } = updateTranslationKeyDto;

    // Check if key exists
    const existingKey = await this.models.TranslationKeyModel.findOne({
      _id: new Types.ObjectId(id)
    });

    if (!existingKey) {
      throw new HttpException('Translation key not found', HttpStatus.NOT_FOUND);
    }

    // If key is being changed, check for duplicates
    if (key && key !== existingKey.key) {
      const duplicate = await this.models.TranslationKeyModel.findOne({ key });
      if (duplicate) {
        throw new HttpException(
          'Translation key already exists',
          HttpStatus.BAD_REQUEST,
        );
      }
    }

    // Update translation key
    const updateData: any = {
      updated_at: +new Date()
    };
    if (key) updateData.key = key;
    if (default_text) updateData.default_text = default_text;

    const updated = await this.models.TranslationKeyModel.findOneAndUpdate(
      { _id: new Types.ObjectId(id) },
      { $set: updateData },
      { new: true }
    );

    // If language_code and translation_value provided, update/create translation
    if (language_code && translation_value) {
      const language = await this.models.LanguageModel.findOne({ code: language_code });
      if (language) {
        const existingTranslation = await this.models.TranslationModel.findOne({
          language_id: language._id,
          key_id: existingKey._id
        });

        if (existingTranslation) {
          existingTranslation.value = translation_value;
          existingTranslation.updated_at = +new Date();
          await existingTranslation.save();
        } else {
          const translation = new this.models.TranslationModel({
            language_id: language._id,
            key_id: existingKey._id,
            value: translation_value,
            created_at: +new Date(),
            updated_at: +new Date()
          });
          await translation.save();
        }
      }
    }

    return updated;
  }

  async deleteTranslationKey(id: string) {
    const existingKey = await this.models.TranslationKeyModel.findOne({
      _id: new Types.ObjectId(id)
    });

    if (!existingKey) {
      throw new HttpException('Translation key not found', HttpStatus.NOT_FOUND);
    }

    // Delete the key (translations will be cascade deleted if you set up the schema properly)
    await this.models.TranslationKeyModel.deleteOne({
      _id: new Types.ObjectId(id)
    });

    // Also delete associated translations
    await this.models.TranslationModel.deleteMany({
      key_id: new Types.ObjectId(id)
    });

    return { message: 'Translation key deleted successfully' };
  }

  async getTranslations(languageId: string, page: number = 1, limit: number = 10, search?: string) {
    await this.findOne(languageId);

    const skip = (page - 1) * limit;
    const languageObjectId = new Types.ObjectId(languageId);

    const matchQuery: any = {};
    
    if (search) {
      matchQuery.$or = [
        { key: { $regex: search, $options: 'i' } },
        { default_text: { $regex: search, $options: 'i' } }
      ];
    }

    const pipeline = [
      {
        $lookup: {
          from: 'translations',
          let: { keyId: '$_id' },
          pipeline: [
            {
              $match: {
                $expr: { $eq: ['$key_id', '$$keyId'] },
                language_id: languageObjectId
              }
            }
          ],
          as: 'translation'
        }
      },
      {
        $match: matchQuery
      },
      {
        $sort: { created_at: -1 as const }
      },
      {
        $skip: skip
      },
      {
        $limit: limit
      },
      {
        $project: {
          key_id: '$_id',
          key: '$key',
          default_text: '$default_text',
          translated_text: { $arrayElemAt: ['$translation.value', 0] }
        }
      }
    ];

    const countPipeline = [
      {
        $match: matchQuery
      },
      {
        $count: 'total'
      }
    ];

    const [data, countResult] = await Promise.all([
      this.models.TranslationKeyModel.aggregate(pipeline),
      this.models.TranslationKeyModel.aggregate(countPipeline)
    ]);

    const total = countResult.length > 0 ? countResult[0].total : 0;

    const formattedData = data.map((item) => ({
      key_id: item.key_id,
      key: item.key,
      default_text: item.default_text,
      translated_text: item.translated_text || ''
    }));

    return this.commonService.paginatedResponse(
      'Translations retrieved successfully',
      formattedData,
      total,
      page,
      limit
    );
  }

  async updateTranslation(
    updateTranslationDto: UpdateTranslationDto,
  ) {
    console.log("updateTranslationDto" , updateTranslationDto)
    const { key_id, value } = updateTranslationDto;
    const existing = await this.models.TranslationModel.findOne({
      language_id: new Types.ObjectId(updateTranslationDto.languageId),
      key_id: new Types.ObjectId(key_id)
    });

    if (existing) {
      existing.value = value;
      existing.updated_at = +new Date();
      return await existing.save();
    } else {
      const translation = new this.models.TranslationModel({
        language_id: new Types.ObjectId(updateTranslationDto.languageId),
        key_id: new Types.ObjectId(key_id),
        value,
        created_at: +new Date(),
        updated_at: +new Date()
      });
      return await translation.save();
    }
  }

  async exportTranslations(languageId: string) {
    const {data : language} = await this.findOne(languageId);

    const keys = await this.models.TranslationKeyModel.find().lean();
    const translations = await this.models.TranslationModel.find({
      language_id: new Types.ObjectId(languageId)
    }).lean();

    const translationMap = new Map();
    translations.forEach(t => {
      translationMap.set(t.key_id.toString(), t.value);
    });

    const data = keys.map(k => ({
      Key: k.key,
      'English (Default)': k.default_text,
      [`Translation (${language.name})`]: translationMap.get(k._id.toString()) || ''
    }));

    const fields = [
      'Key',
      'English (Default)',
      `Translation (${language.name})`
    ];

    return parse(data, { fields });
  }

  async importTranslations(languageId: string, fileBuffer: Buffer) {
    try{

      console.log("importTranslations")
      await this.findOne(languageId);
  
      const results: any[] = [];
      const stream = Readable.from(fileBuffer);
  
      new Promise((resolve, reject) => {
        stream
          .pipe(csv())
          .on('data', (data) => results.push(data))
          .on('end', async () => {
            try {
              for (const row of results) {
                const key = row['Key'];
                const defaultText = row['English (Default)'];
                const translationValue = Object.values(row)[2] as string;
  
                if (!key) continue;
  
                let translationKey = await this.models.TranslationKeyModel.findOne({
                  key
                });
  
                if (!translationKey) {
                  translationKey = new this.models.TranslationKeyModel({
                    key,
                    default_text: defaultText || key,
                    created_at: +new Date(),
                    updated_at: +new Date()
                  });
                  translationKey = await translationKey.save();
                } else if (defaultText && translationKey.default_text !== defaultText) {
                  translationKey.default_text = defaultText;
                  translationKey.updated_at = +new Date();
                  await translationKey.save();
                }
  
                if (translationValue !== undefined) {
                  const existing = await this.models.TranslationModel.findOne({
                    language_id: new Types.ObjectId(languageId),
                    key_id: translationKey._id
                  });
  
                  if (existing) {
                    existing.value = translationValue;
                    existing.updated_at = +new Date();
                    await existing.save();
                  } else {
                    const translation = new this.models.TranslationModel({
                      language_id: new Types.ObjectId(languageId),
                      key_id: translationKey._id,
                      value: translationValue,
                      created_at: +new Date(),
                      updated_at: +new Date()
                    });
                    await translation.save();
                  }
                }
              }
              resolve({ message: 'Translations imported successfully' });
            } catch (error) {
              console.error(error.message);
              if (error instanceof HttpException) throw error;
              throw new HttpException(
                error.message,
                HttpStatus.INTERNAL_SERVER_ERROR,
              );
            }
          })
          .on('error', (error) => reject(error));
      });
  
      return { message: 'Translations imported successfully' };
    }catch(error){
      console.log(error);
      throw new HttpException(
        error.message,
        HttpStatus.INTERNAL_SERVER_ERROR,
      );
    }
  }

  async getPublicTranslations(code: string) {
    const language = await this.models.LanguageModel.findOne({
      code,
      status: true
    }).lean();

    if (!language) {
      throw new HttpException(
        'Language not found or inactive',
        HttpStatus.NOT_FOUND,
      );
    }

    const translations = await this.models.TranslationModel.find({
      language_id: language._id
    }).lean();

    const keys = await this.models.TranslationKeyModel.find().lean();

    const keyMap = new Map();
    keys.forEach(k => {
      keyMap.set(k._id.toString(), k.key);
    });

    const result = {};
    translations.forEach(t => {
      const key = keyMap.get(t.key_id.toString());
      if (key) {
        result[key] = t.value;
      }
    });

    return result;
  }

  async seed() {
    let english = await this.models.LanguageModel.findOne({
      code: 'en'
    });

    if (!english) {
      english = new this.models.LanguageModel({
        name: 'English',
        code: 'en',
        text_direction: 'LTR',
        status: true,
        is_default: true,
        created_at: +new Date(),
        updated_at: +new Date()
      });
      english = await english.save();
    }

    const initialKeys = [
      { key: 'login_button', default_text: 'Login' },
      { key: 'signup_button', default_text: 'Sign Up' },
      { key: 'email_placeholder', default_text: 'Enter your email' },
      { key: 'password_placeholder', default_text: 'Enter your password' },
      { key: 'forgot_password', default_text: 'Forgot password?' },
      { key: 'welcome_back', default_text: 'Welcome back!' },
      { key: 'logout_menu', default_text: 'Logout' },
      { key: 'settings_menu', default_text: 'Settings' },
      { key: 'login_title', default_text: 'Login' },
      { key: 'welcome_msg', default_text: 'Welcome back!' },
      { key: 'items_per_page', default_text: 'Items per page' },
      { key: 'next_page', default_text: 'Next' },
      { key: 'prev_page', default_text: 'Prev' },
    ];

    for (const item of initialKeys) {
      let translationKey = await this.models.TranslationKeyModel.findOne({
        key: item.key
      });

      if (!translationKey) {
        translationKey = new this.models.TranslationKeyModel({
          ...item,
          created_at: +new Date(),
          updated_at: +new Date()
        });
        translationKey = await translationKey.save();
      }

      const existingTranslation = await this.models.TranslationModel.findOne({
        language_id: english._id,
        key_id: translationKey._id
      });

      if (!existingTranslation) {
        const translation = new this.models.TranslationModel({
          language_id: english._id,
          key_id: translationKey._id,
          value: item.default_text,
          created_at: +new Date(),
          updated_at: +new Date()
        });
        await translation.save();
      }
    }

    return { message: 'Default language and keys seeded successfully' };
  }
}
