import sys
import os
import re
import sqlite3
from datetime import datetime
from PyQt5.QtWidgets import (QApplication, QWidget, QLabel, QLineEdit, QPushButton,
                             QVBoxLayout, QHBoxLayout, QFileDialog, QMessageBox, QProgressBar,
                             QGroupBox, QListWidget, QTableWidget, QTableWidgetItem, QTabWidget)
from PyQt5.QtCore import Qt, QThread, pyqtSignal
from PyQt5.QtGui import QIcon
from pytube import YouTube, Playlist
import qdarkstyle

class DownloadThread(QThread):
    progress = pyqtSignal(int)
    finished = pyqtSignal(str)
    log = pyqtSignal(str)

    def __init__(self, url, destination):
        super().__init__()
        self.url = url
        self.destination = destination
        self.is_playlist = 'playlist' in url or 'list=' in url

    def run(self):
        try:
            if self.is_playlist:
                self.download_playlist()
            else:
                self.download_video()
        except Exception as e:
            self.finished.emit(f"Error: {e}")

    def download_video(self):
        yt = YouTube(self.url)
        stream = yt.streams.filter(only_audio=True).order_by('abr').desc().first()
        self.download_and_rename(stream, self.destination, yt.title)
        self.finished.emit(f"{yt.title} has been successfully downloaded.")

    def download_playlist(self):
        pl = Playlist(self.url)
        playlist_title = self.sanitize_filename(pl.title)
        playlist_path = os.path.join(self.destination, playlist_title)
        os.makedirs(playlist_path, exist_ok=True)
        
        total_videos = len(pl.video_urls)
        for count, video in enumerate(pl.videos, start=1):
            try:
                stream = video.streams.filter(only_audio=True).order_by('abr').desc().first()
                self.download_and_rename(stream, playlist_path, video.title)
                self.progress.emit(int((count / total_videos) * 100))
                self.log.emit(f"Downloaded: {video.title}")
            except Exception as e:
                self.log.emit(f"Error downloading {video.title}: {str(e)}")
        self.finished.emit(f"Playlist '{pl.title}' has been downloaded to {playlist_path}.")

    def download_and_rename(self, stream, destination: str, title: str):
        filename = self.sanitize_filename(title)
        out_file = stream.download(output_path=destination, filename=filename)
        base, ext = os.path.splitext(out_file)
        new_file = base + '.mp3'
        os.rename(out_file, new_file)

    def sanitize_filename(self, filename):
        return re.sub(r'[\\/*?:"<>|]', "", filename)

class YouTubeDownloader(QWidget):
    def __init__(self):
        super().__init__()
        self.create_database()  # Create database first
        self.initUI()
        self.last_directory = self.get_last_directory()
        self.is_dark_mode = self.get_dark_mode_preference()
        self.apply_stylesheet()
        
        if self.last_directory:
            self.dest_entry.setText(self.last_directory)

    def initUI(self):
        self.setWindowTitle('YouTube Audio Downloader By RedLionX')
        self.setGeometry(100, 100, 800, 600)
        self.setWindowIcon(QIcon('youtube_icon.png'))

        main_layout = QVBoxLayout()

        # Tabs
        self.tabs = QTabWidget()
        self.tabs.addTab(self.create_download_tab(), "Download")
        self.tabs.addTab(self.create_history_tab(), "History")
        main_layout.addWidget(self.tabs)

        # Dark mode toggle
        self.dark_mode_button = QPushButton('Toggle Dark Mode', self)
        self.dark_mode_button.clicked.connect(self.toggle_dark_mode)
        main_layout.addWidget(self.dark_mode_button)

        self.setLayout(main_layout)

    def create_download_tab(self):
        download_widget = QWidget()
        layout = QVBoxLayout()

        # URL input
        url_group = QGroupBox("URL")
        url_layout = QHBoxLayout()
        self.url_entry = QLineEdit(self)
        self.url_entry.setPlaceholderText("Enter YouTube URL (video or playlist)")
        url_layout.addWidget(self.url_entry)
        url_group.setLayout(url_layout)
        layout.addWidget(url_group)

        # Destination directory
        dest_group = QGroupBox("Destination")
        dest_layout = QHBoxLayout()
        self.dest_entry = QLineEdit(self)
        self.dest_entry.setPlaceholderText("Select destination directory")
        self.dest_button = QPushButton('Browse', self)
        self.dest_button.clicked.connect(self.browse_directory)
        dest_layout.addWidget(self.dest_entry)
        dest_layout.addWidget(self.dest_button)
        dest_group.setLayout(dest_layout)
        layout.addWidget(dest_group)

        # Download button and progress bar
        download_layout = QHBoxLayout()
        self.download_button = QPushButton('Download', self)
        self.download_button.clicked.connect(self.download_media)
        self.progress_bar = QProgressBar(self)
        download_layout.addWidget(self.download_button)
        download_layout.addWidget(self.progress_bar)
        layout.addLayout(download_layout)

        # Log area
        log_group = QGroupBox("Download Log")
        log_layout = QVBoxLayout()
        self.log_list = QListWidget(self)
        log_layout.addWidget(self.log_list)
        log_group.setLayout(log_layout)
        layout.addWidget(log_group)

        download_widget.setLayout(layout)
        return download_widget

    def create_history_tab(self):
        history_widget = QWidget()
        layout = QVBoxLayout()

        self.history_table = QTableWidget()
        self.history_table.setColumnCount(4)
        self.history_table.setHorizontalHeaderLabels(["Title", "URL", "Date", "Status"])
        self.history_table.horizontalHeader().setStretchLastSection(True)
        layout.addWidget(self.history_table)

        self.load_download_history()

        history_widget.setLayout(layout)
        return history_widget

    def apply_stylesheet(self):
        if self.is_dark_mode:
            self.setStyleSheet(qdarkstyle.load_stylesheet_pyqt5())
        else:
            self.setStyleSheet("")
        
        # Custom styles with red accent
        red_accent = "#FF4136"
        self.setStyleSheet(self.styleSheet() + f"""
            QGroupBox {{
                font-weight: bold;
                border: 1px solid {red_accent};
                border-radius: 5px;
                margin-top: 10px;
            }}
            QGroupBox::title {{
                subcontrol-origin: margin;
                left: 10px;
                padding: 0 3px 0 3px;
            }}
            QPushButton {{
                font-weight: bold;
                background-color: {red_accent};
                color: white;
            }}
            QProgressBar::chunk {{
                background-color: {red_accent};
            }}
        """)

    def create_database(self):
        self.conn = sqlite3.connect('downloader.db')
        self.cursor = self.conn.cursor()
        self.cursor.execute('''CREATE TABLE IF NOT EXISTS settings
                               (id INTEGER PRIMARY KEY, last_directory TEXT, dark_mode INTEGER)''')
        self.cursor.execute('''CREATE TABLE IF NOT EXISTS download_history
                               (id INTEGER PRIMARY KEY, title TEXT, url TEXT, date TEXT, status TEXT)''')
        self.conn.commit()

    def get_last_directory(self) -> str:
        self.cursor.execute("SELECT last_directory FROM settings WHERE id=1")
        result = self.cursor.fetchone()
        return result[0] if result else ''

    def get_dark_mode_preference(self) -> bool:
        self.cursor.execute("SELECT dark_mode FROM settings WHERE id=1")
        result = self.cursor.fetchone()
        return bool(result[0]) if result else False

    def save_last_directory(self, directory: str):
        if self.cursor.execute("SELECT * FROM settings WHERE id=1").fetchone():
            self.cursor.execute("UPDATE settings SET last_directory = ? WHERE id=1", (directory,))
        else:
            self.cursor.execute("INSERT INTO settings (id, last_directory) VALUES (1, ?)", (directory,))
        self.conn.commit()

    def save_dark_mode_preference(self, is_dark_mode: bool):
        if self.cursor.execute("SELECT * FROM settings WHERE id=1").fetchone():
            self.cursor.execute("UPDATE settings SET dark_mode = ? WHERE id=1", (int(is_dark_mode),))
        else:
            self.cursor.execute("INSERT INTO settings (id, dark_mode) VALUES (1, ?)", (int(is_dark_mode),))
        self.conn.commit()

    def browse_directory(self):
        directory = QFileDialog.getExistingDirectory(self, 'Select Directory')
        if directory:
            self.dest_entry.setText(directory)
            self.save_last_directory(directory)

    def is_valid_url(self, url: str) -> bool:
        youtube_regex = re.compile(r'^(https?\:\/\/)?(www\.youtube\.com|youtu\.?be|music\.youtube\.com)\/.+$')
        return bool(youtube_regex.match(url))

    def download_media(self):
        url = self.url_entry.text()
        if not self.is_valid_url(url):
            QMessageBox.critical(self, "Invalid URL", "Please enter a valid YouTube or YouTube Music URL.")
            return

        destination = self.dest_entry.text() or '.'

        self.download_thread = DownloadThread(url, destination)
        self.download_thread.progress.connect(self.progress_bar.setValue)
        self.download_thread.finished.connect(self.on_download_finished)
        self.download_thread.log.connect(self.update_log)
        self.download_thread.start()

        self.download_button.setEnabled(False)
        self.add_to_history(url, "In Progress")

    def on_download_finished(self, message):
        self.progress_bar.setValue(100)
        QMessageBox.information(self, "Download Finished", message)
        self.save_last_directory(self.dest_entry.text())
        self.download_button.setEnabled(True)
        self.update_history_status(self.url_entry.text(), "Completed")
        self.load_download_history()

    def update_log(self, message):
        self.log_list.addItem(message)
        self.log_list.scrollToBottom()

    def toggle_dark_mode(self):
        self.is_dark_mode = not self.is_dark_mode
        self.apply_stylesheet()
        self.save_dark_mode_preference(self.is_dark_mode)

    def add_to_history(self, url, status):
        title = "Unknown"
        try:
            if 'playlist' in url or 'list=' in url:
                title = Playlist(url).title
            else:
                title = YouTube(url).title
        except:
            pass
        date = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        self.cursor.execute("INSERT INTO download_history (title, url, date, status) VALUES (?, ?, ?, ?)",
                            (title, url, date, status))
        self.conn.commit()

    def update_history_status(self, url, status):
        self.cursor.execute("UPDATE download_history SET status = ? WHERE url = ?", (status, url))
        self.conn.commit()

    def load_download_history(self):
        self.cursor.execute("SELECT title, url, date, status FROM download_history ORDER BY date DESC")
        history = self.cursor.fetchall()
        self.history_table.setRowCount(len(history))
        for row, item in enumerate(history):
            for col, value in enumerate(item):
                self.history_table.setItem(row, col, QTableWidgetItem(str(value)))
        self.history_table.resizeColumnsToContents()

if __name__ == "__main__":
    app = QApplication(sys.argv)
    app.setStyle('Fusion')
    downloader = YouTubeDownloader()
    downloader.show()
    sys.exit(app.exec_()) /improve code