import { App, TFolder, Menu, TAbstractFile, Notice, TFile, Editor, MarkdownView, Platform, stringifyYaml } from 'obsidian';
import FolderNotesPlugin from './main';
import { getFolderNote, createFolderNote, deleteFolderNote, turnIntoFolderNote, openFolderNote, extractFolderName } from './functions/folderNoteFunctions';
import { ExcludedFolder } from './ExcludeFolders/ExcludeFolder';
import { getFolderPathFromString } from './functions/utils';

export class Commands {
	plugin: FolderNotesPlugin;
	app: App;
	constructor(app: App, plugin: FolderNotesPlugin) {
		this.plugin = plugin;
		this.app = app;
	}
	registerCommands() {
		this.editorCommands();
		this.fileCommands();
		this.regularCommands();
	}
	regularCommands() {
		this.plugin.addCommand({
			id: 'turn-into-folder-note',
			name: 'Make current active note a folder note for the folder of the active note',
			callback: () => {
				const file = this.app.workspace.getActiveFile();
				if (!(file instanceof TFile)) return;
				const folder = file.parent;
				if (!(folder instanceof TFolder)) return;
				const folderNote = getFolderNote(this.plugin, folder.path);
				turnIntoFolderNote(this.plugin, file, folder, folderNote);
			}
		});
		this.plugin.addCommand({
			id: 'create-folder-note',
			name: 'Create folder note with a new folder for the active note in the current folder',
			callback: async () => {
				const file = this.app.workspace.getActiveFile();
				if (!(file instanceof TFile)) return;
				let newPath = file.parent?.path + '/' + file.basename;
				if (file.parent?.path === '' || file.parent?.path === '/') {
					newPath = file.basename;
				}
				if (this.plugin.app.vault.getAbstractFileByPath(newPath)) {
					return new Notice('Folder already exists');
				}
				const automaticallyCreateFolderNote = this.plugin.settings.autoCreate;
				this.plugin.settings.autoCreate = false;
				this.plugin.saveSettings();
				await this.plugin.app.vault.createFolder(newPath);
				const folder = this.plugin.app.vault.getAbstractFileByPath(newPath);
				if (!(folder instanceof TFolder)) return;
				createFolderNote(this.plugin, folder.path, true, '.' + file.extension, false, file);
				this.plugin.settings.autoCreate = automaticallyCreateFolderNote;
				this.plugin.saveSettings();
			}
		})
		this.plugin.addCommand({
			id: 'create-folder-note-for-current-folder',
			name: 'Create markdown folder note for current folder of active note',
			callback: () => {
				const file = this.app.workspace.getActiveFile();
				if (!(file instanceof TFile)) return;
				const folder = file.parent;
				if (!(folder instanceof TFolder)) return;
				createFolderNote(this.plugin, folder.path, true, '.md', false);
			}
		});

		this.plugin.settings.supportedFileTypes.forEach((fileType) => {
			if (fileType === 'md') return;
			this.plugin.addCommand({
				id: `create-${fileType}-folder-note-for-current-folder`,
				name: `Create ${fileType} folder note for current folder of active note`,
				callback: () => {
					const file = this.app.workspace.getActiveFile();
					if (!(file instanceof TFile)) return;
					const folder = file.parent;
					if (!(folder instanceof TFolder)) return;
					createFolderNote(this.plugin, folder.path, true, '.' + fileType, false);
				}
			});
		});

		this.plugin.addCommand({
			id: 'delete-folder-note-for-current-folder',
			name: 'Delete folder note of current folder of active note',
			callback: () => {
				const file = this.app.workspace.getActiveFile();
				if (!(file instanceof TFile)) return;
				const folder = file.parent;
				if (!(folder instanceof TFolder)) return;
				const folderNote = getFolderNote(this.plugin, folder.path);
				if (!(folderNote instanceof TFile)) return;
				deleteFolderNote(this.plugin, folderNote, true);
			}
		});
		this.plugin.addCommand({
			id: 'open-folder-note-for-current-folder',
			name: 'Open folder note of current folder of active note',
			callback: () => {
				const file = this.app.workspace.getActiveFile();
				if (!(file instanceof TFile)) return;
				const folder = file.parent;
				if (!(folder instanceof TFolder)) return;
				const folderNote = getFolderNote(this.plugin, folder.path);
				if (!(folderNote instanceof TFile)) return;
				openFolderNote(this.plugin, folderNote);
			}
		});
		this.plugin.addCommand({
			id: 'insert-folder-overview-fn',
			name: 'Insert folder overview',
			editorCheckCallback: (checking: boolean, editor: Editor) => {
				const line = editor.getCursor().line;
				const lineText = editor.getLine(line);
				if (lineText.trim() === '' || lineText.trim() === '>') {
					if (!checking) {
						let json = Object.assign({}, this.plugin.settings.defaultOverview);
						json.id = crypto.randomUUID();
						const yaml = stringifyYaml(json)
						if (lineText.trim() === '') {
							editor.replaceSelection(`\`\`\`folder-overview\n${yaml}\`\`\`\n`);
						} else if (lineText.trim() === '>') {
							// add > to the beginning of each line
							const lines = yaml.split('\n');
							const newLines = lines.map((line) => {
								return `> ${line}`;
							});
							editor.replaceSelection(`\`\`\`folder-overview\n${newLines.join('\n')}\`\`\`\n`);
						}
					}
					return true;
				}
				return false;
			},
		})
		this.plugin.addCommand({
			id: 'create-folder-note-from-selected-text',
			name: 'Create folder note from selected text',
			editorCheckCallback: (checking: boolean, editor: Editor, view: MarkdownView) => {
				const text = editor.getSelection().trim();
				const line = editor.getCursor().line;
				const file = view.file;
				if (!(file instanceof TFile)) return false;
				if (text && text.trim() !== '') {
					if (checking) { return true; }
					const blacklist = ['*', '\\', '"', '/', '<', '>', '?', '|', ':'];
					for (const char of blacklist) {
						if (text.includes(char)) {
							new Notice('File name cannot contain any of the following characters: * " \\ / < > : | ?');
							return false;
						}
					}
					if (text.endsWith('.')) {
						new Notice('File name cannot end with a dot');
						return;
					}
					let folder: TAbstractFile | null;
					const folderPath = getFolderPathFromString(file.path);
					if (folderPath === '') {
						folder = this.plugin.app.vault.getAbstractFileByPath(text);
						if (folder instanceof TFolder) {
							new Notice('Folder note already exists');
							return false;
						} else {
							this.plugin.app.vault.createFolder(text);
							createFolderNote(this.plugin, text, false);
						}
					} else {
						folder = this.plugin.app.vault.getAbstractFileByPath(folderPath + '/' + text);
						if (folder instanceof TFolder) {
							new Notice('Folder note already exists');
							return false;
						}
						if (this.plugin.settings.storageLocation === 'parentFolder') {
							if (this.app.vault.getAbstractFileByPath(folderPath + '/' + text + this.plugin.settings.folderNoteType)) {
								new Notice('File already exists');
								return false;
							}
						}
						this.plugin.app.vault.createFolder(folderPath + '/' + text);
						createFolderNote(this.plugin, folderPath + '/' + text, false);
					}
					const fileName = this.plugin.settings.folderNoteName.replace('{{folder_name}}', text);
					if (fileName !== text) {
						editor.replaceSelection(`[[${fileName}]]`);
					} else {
						editor.replaceSelection(`[[${fileName}|${text}]]`);
					}
					return true;
				}
				return false;
			},
		})
	}

	fileCommands() {
		this.plugin.registerEvent(this.app.workspace.on('file-menu', (menu: Menu, file: TAbstractFile) => {
			let folder: TAbstractFile | TFolder | null = file.parent;
			if (file instanceof TFile) {
				if (this.plugin.settings.storageLocation === 'insideFolder') {
					folder = file.parent;
				} else {
					const fileName = extractFolderName(this.plugin.settings.folderNoteName, file.basename);
					if (fileName) {
						if (file.parent?.path === '' || file.parent?.path === '/') {
							folder = this.plugin.app.vault.getAbstractFileByPath(fileName);
						} else {
							folder = this.plugin.app.vault.getAbstractFileByPath(file.parent?.path + '/' + fileName);
						}
					}
				}
				if (folder instanceof TFolder) {
					const folderNote = getFolderNote(this.plugin, folder.path);
					if (folderNote?.path === file.path) { return; }
				} else if (file.parent instanceof TFolder) {
					folder = file.parent;
				}
			}
			menu.addItem((item) => {
				if (Platform.isDesktop && !Platform.isTablet && this.plugin.settings.useSubmenus) {
					item
						.setTitle('Folder Note Commands')
						.setIcon('folder-edit');
				}
				let subMenu: Menu;
				if (!Platform.isDesktopApp || !Platform.isDesktop || Platform.isTablet || !this.plugin.settings.useSubmenus) {
					subMenu = menu;
					item.setDisabled(true);
				} else {
					// @ts-ignore
					subMenu = item.setSubmenu() as Menu;
				}
				if (file instanceof TFile) {
					// @ts-ignore
					subMenu.addItem((item) => {
						item.setTitle('Create folder note')
							.setIcon('edit')
							.onClick(async () => {
								if (!folder) return;
								let newPath = folder.path + '/' + file.basename;
								if (folder.path === '' || folder.path === '/') {
									newPath = file.basename;
								}
								if (this.plugin.app.vault.getAbstractFileByPath(newPath)) {
									return new Notice('Folder already exists');
								}
								const automaticallyCreateFolderNote = this.plugin.settings.autoCreate;
								this.plugin.settings.autoCreate = false;
								this.plugin.saveSettings();
								await this.plugin.app.vault.createFolder(newPath);
								const newFolder = this.plugin.app.vault.getAbstractFileByPath(newPath);
								if (!(newFolder instanceof TFolder)) return;
								await createFolderNote(this.plugin, newFolder.path, true, '.' + file.extension, false, file);
								this.plugin.settings.autoCreate = automaticallyCreateFolderNote;
								this.plugin.saveSettings();
							});
					});
					if (getFolderPathFromString(file.path) === '') return;
					if (!(folder instanceof TFolder)) return;
					subMenu.addItem((item) => {
						item.setTitle(`Turn into folder note for ${folder?.name}`)
							.setIcon('edit')
							.onClick(() => {
								if (!folder || !(folder instanceof TFolder)) return;
								const folderNote = getFolderNote(this.plugin, folder.path);
								turnIntoFolderNote(this.plugin, file, folder, folderNote);
							});
					});
				}
				if (!(file instanceof TFolder)) return;
				if (this.plugin.settings.excludeFolders.find((folder) => folder.path === file.path)) {
					subMenu.addItem((item) => {
						item.setTitle('Remove folder from excluded folders')
							.setIcon('trash')
							.onClick(() => {
								this.plugin.settings.excludeFolders = this.plugin.settings.excludeFolders.filter(
									(folder) => folder.path !== file.path);
								this.plugin.saveSettings();
								new Notice('Successfully removed folder from excluded folders');
							});
					});
					return;
				}
				subMenu.addItem((item) => {
					item.setTitle('Exclude folder from folder notes')
						.setIcon('x-circle')
						.onClick(() => {
							const excludedFolder = new ExcludedFolder(file.path, this.plugin.settings.excludeFolders.length, this.plugin);
							this.plugin.settings.excludeFolders.push(excludedFolder);
							this.plugin.saveSettings();
							new Notice('Successfully excluded folder from folder notes');
						});
				});
				if (!(file instanceof TFolder)) return;
				const folderNote = getFolderNote(this.plugin, file.path);
				if (folderNote instanceof TFile) {
					subMenu.addItem((item) => {
						item.setTitle('Delete folder note')
							.setIcon('trash')
							.onClick(() => {
								deleteFolderNote(this.plugin, folderNote, true);
							});
					});

					subMenu.addItem((item) => {
						item.setTitle('Open folder note')
							.setIcon('chevron-right-square')
							.onClick(() => {
								openFolderNote(this.plugin, folderNote);
							});
					});

					subMenu.addItem((item) => {
						item.setTitle('Copy Obsidian URL')
							.setIcon('link')
							.onClick(() => {
								// @ts-ignore
								this.app.copyObsidianUrl(folderNote);
							});
					});

				} else {
					subMenu.addItem((item) => {
						item.setTitle('Create markdown folder note')
							.setIcon('edit')
							.onClick(() => {
								createFolderNote(this.plugin, file.path, true, '.md');
							});
					});

					this.plugin.settings.supportedFileTypes.forEach((fileType) => {
						if (fileType === 'md') return;
						subMenu.addItem((item) => {
							item.setTitle(`Create ${fileType} folder note`)
								.setIcon('edit')
								.onClick(() => {
									createFolderNote(this.plugin, file.path, true, '.' + fileType);
								});
						});
					});
				}
			});
		}));
	}

	editorCommands() {
		this.plugin.registerEvent(this.plugin.app.workspace.on('editor-menu', (menu: Menu, editor: Editor, view: MarkdownView) => {
			const text = editor.getSelection().trim();
			const line = editor.getCursor().line;
			const lineText = editor.getLine(line);
			if (lineText.trim() === '' || lineText.trim() === '>') {
				menu.addItem((item) => {
					item.setTitle('Create folder overview')
						.setIcon('edit')
						.onClick(() => {
							let json = Object.assign({}, this.plugin.settings.defaultOverview);
							json.id = crypto.randomUUID();
							const yaml = stringifyYaml(json)
							if (lineText.trim() === '') {
								editor.replaceSelection(`\`\`\`folder-overview\n${yaml}\`\`\`\n`);
							} else if (lineText.trim() === '>') {
								// add > to the beginning of each line
								const lines = yaml.split('\n');
								const newLines = lines.map((line) => {
									return `> ${line}`;
								});
								editor.replaceSelection(`\`\`\`folder-overview\n${newLines.join('\n')}\`\`\`\n`);
							}
						});
				});
			}
			if (!text || text.trim() === '') return;
			menu.addItem((item) => {
				item.setTitle('Create folder note')
					.setIcon('edit')
					.onClick(() => {
						const file = view.file;
						if (!(file instanceof TFile)) return;
						const blacklist = ['*', '\\', '"', '/', '<', '>', '?', '|', ':'];
						for (const char of blacklist) {
							if (text.includes(char)) {
								new Notice('File name cannot contain any of the following characters: * " \\ / < > : | ?');
								return;
							}
						}
						if (text.endsWith('.')) {
							new Notice('File name cannot end with a dot');
							return;
						}
						let folder: TAbstractFile | null;
						const folderPath = getFolderPathFromString(file.path);
						const fileName = this.plugin.settings.folderNoteName.replace('{{folder_name}}', text);
						if (folderPath === '') {
							folder = this.plugin.app.vault.getAbstractFileByPath(text);
							if (folder instanceof TFolder) {
								return new Notice('Folder note already exists');
							} else {
								this.plugin.app.vault.createFolder(text);
								createFolderNote(this.plugin, text, false);
							}
						} else {
							folder = this.plugin.app.vault.getAbstractFileByPath(folderPath + '/' + text);
							if (folder instanceof TFolder) {
								return new Notice('Folder note already exists');
							}
							if (this.plugin.settings.storageLocation === 'parentFolder') {
								if (this.app.vault.getAbstractFileByPath(folderPath + '/' + fileName + this.plugin.settings.folderNoteType)) {
									return new Notice('File already exists');
								}
							}
							this.plugin.app.vault.createFolder(folderPath + '/' + text);
							createFolderNote(this.plugin, folderPath + '/' + text, false);
						}
						if (fileName !== text) {
							editor.replaceSelection(`[[${fileName}]]`);
						} else {
							editor.replaceSelection(`[[${fileName}|${text}]]`);
						}
					});
			});
		}));
	}
}
