import requests
from bs4 import BeautifulSoup
import json
import html
import re

try:
    from playwright.sync_api import sync_playwright, TimeoutError as PlaywrightTimeout  # type: ignore
    PLAYWRIGHT_AVAILABLE = True
except:
    PLAYWRIGHT_AVAILABLE = False


class UGScraper:
    def __init__(self, cookies):
        """
        Initialize with your UG cookies. Get these from browser DevTools.
        Required cookies: SESSIONUG, bbsessionhash
        """
        self.cookies = cookies
        self.user_agent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
    
    def get_favorites(self):
        """Get your favorited tabs. Requires valid cookies."""
        favorites_url = "https://www.ultimate-guitar.com/user/mytabs"
        html_text, error = self._fetch_page(favorites_url)
        
        if error:
            return {'error': error, 'favorites': []}
        
        favorites = self._parse_favorites(html_text)
        return {'favorites': favorites, 'error': None}
    
    def get_tab(self, url):
        """Download a specific tab by URL."""
        html_text, error = self._fetch_page(url)
        
        if error:
            return {'error': error}
        
        title, artist, content, chords_url = self._parse_tab_from_html(html_text)
        
        # Try chords version if main tab is empty
        if not content and chords_url:
            html_text, error = self._fetch_page(chords_url)
            if not error:
                title, artist, content, _ = self._parse_tab_from_html(html_text)
        
        content = self._clean_content(content)
        
        return {
            'title': title,
            'artist': artist,
            'content': content,
            'url': url,
            'error': None if content else 'No content found'
        }
    
    def _fetch_page(self, url):
        headers = {
            'User-Agent': self.user_agent,
            'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
            'Accept-Language': 'en-US,en;q=0.5',
            'Referer': 'https://www.ultimate-guitar.com/'
        }
        
        try:
            response = requests.get(url, headers=headers, cookies=self.cookies, timeout=15)
            html_text = response.text
            
            if response.status_code != 200:
                return None, f'HTTP {response.status_code}'
            
            # Check for Cloudflare
            if self._is_cloudflare(html_text):
                if PLAYWRIGHT_AVAILABLE:
                    html_text = self._fetch_with_playwright(url)
                    if html_text and not self._is_cloudflare(html_text):
                        return html_text, None
                return None, 'Cloudflare blocked'
            
            return html_text, None
            
        except requests.Timeout:
            return None, 'Timeout'
        except Exception as e:
            return None, str(e)
    
    def _is_cloudflare(self, html_text):
        return html_text and ('Just a moment...' in html_text or 'cf_chl_opt' in html_text)
    
    def _parse_favorites(self, html_text):
        favorites = []
        if not html_text:
            return favorites
        
        soup = BeautifulSoup(html_text, 'html.parser')
        store_div = soup.find('div', class_='js-store')
        
        if store_div and store_div.has_attr('data-content'):
            raw_data = store_div.get('data-content', '')
            try:
                decoded = html.unescape(raw_data)
                store_data = json.loads(decoded)
                items = (store_data.get('store', {})
                        .get('page', {})
                        .get('data', {})
                        .get('list', {})
                        .get('list', []))
                
                for item in items:
                    favorites.append({
                        'title': item.get('song_name', 'Unknown'),
                        'artist': item.get('band_name', 'Unknown'),
                        'type': item.get('type', 'Tab'),
                        'url': item.get('song_url', ''),
                        'liked_at': item.get('date')
                    })
            except:
                pass
        
        return favorites
    
    def _parse_tab_from_html(self, html_text):
        title = None
        artist = None
        content = ''
        chords_url = None
        
        soup = BeautifulSoup(html_text, 'html.parser')
        store_div = soup.find('div', class_='js-store')
        
        if store_div and store_div.has_attr('data-content'):
            raw_data = store_div.get('data-content', '')
            try:
                decoded = html.unescape(raw_data)
                store_data = json.loads(decoded)
                title, artist, content, chords_url = self._parse_tab_from_store(store_data)
            except:
                pass
        
        if not content:
            page_data = self._extract_page_data(html_text) or {}
            title, artist, content, chords_url = self._parse_tab_from_store(page_data)
        
        return title, artist, content, chords_url
    
    def _parse_tab_from_store(self, store_data):
        if not isinstance(store_data, dict):
            return None, None, '', None
        
        if 'store' in store_data:
            page_data = (store_data.get('store', {})
                        .get('page', {})
                        .get('data', {}))
        else:
            page_data = store_data.get('data', store_data)
        
        tab_view = page_data.get('tab_view', {})
        tab_data = page_data.get('tab', {})
        
        title = tab_data.get('song_name') or tab_view.get('song_name')
        artist = tab_data.get('artist_name') or tab_view.get('artist_name')
        
        # Get content from wiki_tab structure
        wiki_tab = tab_view.get('wiki_tab', {})
        content = wiki_tab.get('content', '')
        
        if not content:
            content = tab_view.get('content', '') or tab_view.get('tab', '')
        
        chords_url = tab_view.get('type_urls', {}).get('chords')
        
        return title, artist, content, chords_url
    
    def _clean_content(self, text):
        if not text:
            return text
        
        cleaned = text.strip()
        cleaned = re.sub(r'\[/?tab\]', '', cleaned, flags=re.IGNORECASE)
        cleaned = re.sub(r'\[ch\]([^\[]+)\[/ch\]', r'\1', cleaned, flags=re.IGNORECASE)
        
        return cleaned.strip()
    
    def _extract_page_data(self, html_text):
        marker = 'window.UGAPP.store.page'
        idx = html_text.find(marker)
        if idx == -1:
            return None
        
        start = html_text.find('{', idx)
        if start == -1:
            return None
        
        depth = 0
        for i in range(start, len(html_text)):
            ch = html_text[i]
            if ch == '{':
                depth += 1
            elif ch == '}':
                depth -= 1
                if depth == 0:
                    try:
                        return json.loads(html_text[start:i + 1])
                    except:
                        return None
        return None
    
    def _fetch_with_playwright(self, url):
        if not PLAYWRIGHT_AVAILABLE:
            return None
        
        try:
            with sync_playwright() as p:
                browser = p.chromium.launch(headless=True)
                context = browser.new_context()
                
                if self.cookies:
                    cookie_list = []
                    for name, value in self.cookies.items():
                        cookie_list.append({
                            'name': name,
                            'value': value,
                            'domain': '.ultimate-guitar.com',
                            'path': '/'
                        })
                    context.add_cookies(cookie_list)
                
                page = context.new_page()
                try:
                    page.goto(url, wait_until='networkidle', timeout=30000)
                    html = page.content()
                except PlaywrightTimeout:
                    html = page.content()
                finally:
                    browser.close()
                
                return html
        except:
            return None
