import orm from '../entity/orm';
import { att } from '../entity/att';
import { and, eq, isNull, inArray, notInArray } from 'drizzle-orm';
import r2Service from './r2-service';
import constant from '../const/constant';
import fileUtils from '../utils/file-utils';
import { attConst } from '../const/entity-const';
import { parseHTML } from 'linkedom';

const attService = {

	async addAtt(c, attachments) {

		for (let attachment of attachments) {
			await r2Service.putObj(c, attachment.key, attachment.content, {
				contentType: attachment.mimeType,
				contentDisposition: `attachment; filename="${attachment.filename}"`
			});
		}

		await orm(c).insert(att).values(attachments).run();
	},

	list(c, params, userId) {
		const { emailId } = params;

		return orm(c).select().from(att).where(
			and(
				eq(att.emailId, emailId),
				eq(att.userId, userId),
				eq(att.type, attConst.type.ATT),
				isNull(att.contentId)
			)
		).all();
	},

	async toImageUrlHtml(c, content, r2Domain) {

		const { document } = parseHTML(content);

		const images = Array.from(document.querySelectorAll('img'));

		const attDataList = [];

		for (const img of images) {

			const src = img.getAttribute('src');
			if (src && src.startsWith('data:image')) {
				const file = fileUtils.base64ToFile(src);
				const buff = await file.arrayBuffer();
				const key = constant.ATTACHMENT_PREFIX + await fileUtils.getBuffHash(buff) + fileUtils.getExtFileName(file.name);
				img.setAttribute('src', r2Domain + '/' + key);

				const attData = {};
				attData.key = key;
				attData.filename = file.name;
				attData.mimeType = file.type;
				attData.size = file.size;
				attData.buff = buff;

				attDataList.push(attData);
			}

			const hasInlineWidth = img.hasAttribute('width');
			const style = img.getAttribute('style') || '';
			const hasStyleWidth = /(^|\s)width\s*:\s*[^;]+/.test(style);

			if (!hasInlineWidth && !hasStyleWidth) {
				const newStyle = (style ? style.trim().replace(/;$/, '') + '; ' : '') + 'max-width: 100%;';
				img.setAttribute('style', newStyle);
			}
		}
		return { attDataList, html: document.toString() };
	},

	async saveSendAtt(c, attList, userId, accountId, emailId) {

		const attDataList = [];

		for (let att of attList) {
			att.buff = fileUtils.base64ToUint8Array(att.content);
			att.key = constant.ATTACHMENT_PREFIX + await fileUtils.getBuffHash(att.buff) + fileUtils.getExtFileName(att.filename);
			const attData = { userId, accountId, emailId };
			attData.key = att.key;
			attData.size = att.buff.length;
			attData.filename = att.filename;
			attData.mimeType = att.type;
			attData.type = attConst.type.ATT;
			attDataList.push(attData);
		}

		await orm(c).insert(att).values(attDataList).run();

		for (let att of attList) {
			await r2Service.putObj(c, att.key, att.buff, {
				contentType: att.type,
				contentDisposition: `attachment; filename="${att.filename}"`
			});
		}

	},

	async saveArticleAtt(c, attDataList, userId, accountId, emailId) {

		for (let attData of attDataList) {
			attData.userId = userId;
			attData.emailId = emailId;
			attData.accountId = accountId;
			attData.type = attConst.type.EMBED;
			await r2Service.putObj(c, attData.key, attData.buff, {
				contentType: attData.mimeType
			});
		}

		await orm(c).insert(att).values(attDataList).run();

	},

	async removeByUserIds(c, userIds) {
		await this.removeAttByField(c, att.userId, userIds);
	},

	async removeByEmailIds(c, emailIds) {
		await this.removeAttByField(c, att.emailId, emailIds);
	},

	async removeByAccountIds(c, accountIds) {
		await this.removeAttByField(c, att.accountId, accountIds);
	},

	async removeAttByField(c, fieldName, fieldValues) {

		const condition = inArray(fieldName, fieldValues);
		const attList = await orm(c).select().from(att).where(condition).limit(99);

		if (attList.length === 0) {
			return;
		}

		const attIds = attList.map(attRow => attRow.attId);
		const keys = attList.map(attRow => attRow.key);
		await orm(c).delete(att).where(inArray(att.attId, attIds)).run();

		const existAttRows = await orm(c).select().from(att).where(inArray(att.key, keys)).all();
		const existKeys = existAttRows.map(attRow => attRow.key);
		const delKeyList = keys.filter(key => !existKeys.includes(key));
		if (delKeyList.length > 0) {
			await c.env.r2.delete(delKeyList);
		}

		if (attList.length >= 99) {
			await this.removeAttByField(c, fieldName, fieldValues);
		}
	},

	selectByEmailIds(c, emailIds) {
		return orm(c).select().from(att).where(
			and(
				inArray(att.emailId,emailIds),
				eq(att.type, attConst.type.ATT)
			))
			.all();
	}
};

export default attService;
