"""
시놀로지 캘린더 서비스 구현
"""
import asyncio
import aiohttp
from typing import List, Dict, Any, Optional
from datetime import datetime, timedelta
import caldav
from caldav.lib.error import AuthorizationError, NotFoundError
import logging

from .base import (
    BaseCalendarService, CalendarProvider, CalendarInfo, CalendarEvent,
    CalendarCredentials, CalendarServiceError, AuthenticationError,
    CalendarNotFoundError, EventNotFoundError
)

logger = logging.getLogger(__name__)


class SynologyCalendarService(BaseCalendarService):
    """시놀로지 캘린더 서비스"""
    
    def __init__(self, credentials: CalendarCredentials):
        super().__init__(credentials)
        self.dsm_url = credentials.server_url
        self.username = credentials.username
        self.password = credentials.password
        self.session_token = None
        self.caldav_client = None
        
        # CalDAV URL 구성
        if self.dsm_url and self.username:
            self.caldav_url = f"{self.dsm_url}/caldav/{self.username}/"
    
    async def authenticate(self) -> bool:
        """
        시놀로지 DSM 및 CalDAV 인증
        """
        try:
            # 1. DSM API 인증 (선택사항 - 추가 기능용)
            await self._authenticate_dsm()
            
            # 2. CalDAV 인증 (메인)
            await self._authenticate_caldav()
            
            self._authenticated = True
            logger.info(f"시놀로지 캘린더 인증 성공: {self.username}")
            return True
            
        except Exception as e:
            logger.error(f"시놀로지 캘린더 인증 실패: {e}")
            raise AuthenticationError(f"시놀로지 인증 실패: {str(e)}")
    
    async def _authenticate_dsm(self) -> Optional[str]:
        """DSM API 인증 (추가 기능용)"""
        if not self.dsm_url:
            return None
            
        login_url = f"{self.dsm_url}/webapi/auth.cgi"
        params = {
            "api": "SYNO.API.Auth",
            "version": "3",
            "method": "login",
            "account": self.username,
            "passwd": self.password,
            "session": "TodoProject",
            "format": "sid"
        }
        
        try:
            async with aiohttp.ClientSession() as session:
                async with session.get(login_url, params=params, ssl=False) as response:
                    data = await response.json()
                    
                    if data.get("success"):
                        self.session_token = data["data"]["sid"]
                        logger.info("DSM API 인증 성공")
                        return self.session_token
                    else:
                        error_code = data.get("error", {}).get("code", "Unknown")
                        raise AuthenticationError(f"DSM 로그인 실패 (코드: {error_code})")
                        
        except aiohttp.ClientError as e:
            logger.warning(f"DSM API 인증 실패 (CalDAV는 계속 시도): {e}")
            return None
    
    async def _authenticate_caldav(self):
        """CalDAV 인증"""
        try:
            # CalDAV 클라이언트 생성
            self.caldav_client = caldav.DAVClient(
                url=self.caldav_url,
                username=self.username,
                password=self.password
            )
            
            # 연결 테스트
            principal = self.caldav_client.principal()
            calendars = principal.calendars()
            
            logger.info(f"CalDAV 인증 성공: {len(calendars)}개 캘린더 발견")
            
        except AuthorizationError as e:
            raise AuthenticationError(f"CalDAV 인증 실패: 사용자명 또는 비밀번호가 잘못되었습니다")
        except Exception as e:
            raise AuthenticationError(f"CalDAV 연결 실패: {str(e)}")
    
    async def get_calendars(self) -> List[CalendarInfo]:
        """캘린더 목록 조회"""
        self._ensure_authenticated()
        
        try:
            principal = self.caldav_client.principal()
            calendars = principal.calendars()
            
            calendar_list = []
            for calendar in calendars:
                try:
                    # 캘린더 속성 조회
                    props = calendar.get_properties([
                        caldav.dav.DisplayName(),
                        caldav.elements.icalendar.CalendarColor(),
                        caldav.elements.icalendar.CalendarDescription(),
                    ])
                    
                    name = props.get(caldav.dav.DisplayName.tag, "Unknown Calendar")
                    color = props.get(caldav.elements.icalendar.CalendarColor.tag, "#6366f1")
                    description = props.get(caldav.elements.icalendar.CalendarDescription.tag, "")
                    
                    # 색상 형식 정규화
                    if color and not color.startswith('#'):
                        color = f"#{color}"
                    
                    calendar_info = CalendarInfo(
                        id=calendar.url,
                        name=name,
                        color=color or "#6366f1",
                        description=description,
                        provider=CalendarProvider.SYNOLOGY,
                        is_writable=True  # 시놀로지 캘린더는 일반적으로 쓰기 가능
                    )
                    
                    calendar_list.append(calendar_info)
                    
                except Exception as e:
                    logger.warning(f"캘린더 정보 조회 실패: {e}")
                    continue
            
            logger.info(f"시놀로지 캘린더 {len(calendar_list)}개 조회 완료")
            return calendar_list
            
        except Exception as e:
            logger.error(f"캘린더 목록 조회 실패: {e}")
            raise CalendarServiceError(f"캘린더 목록 조회 실패: {str(e)}")
    
    async def create_event(self, calendar_id: str, event: CalendarEvent) -> CalendarEvent:
        """이벤트 생성"""
        self._ensure_authenticated()
        
        try:
            # 캘린더 객체 가져오기
            calendar = self.caldav_client.calendar(url=calendar_id)
            
            # ICS 형식으로 이벤트 생성
            ics_content = self._event_to_ics(event)
            
            # 이벤트 추가
            caldav_event = calendar.add_event(ics_content)
            
            # 생성된 이벤트 ID 설정
            event.id = caldav_event.url
            
            logger.info(f"시놀로지 캘린더 이벤트 생성 완료: {event.title}")
            return event
            
        except NotFoundError:
            raise CalendarNotFoundError(f"캘린더를 찾을 수 없습니다: {calendar_id}")
        except Exception as e:
            logger.error(f"이벤트 생성 실패: {e}")
            raise CalendarServiceError(f"이벤트 생성 실패: {str(e)}")
    
    async def update_event(self, calendar_id: str, event: CalendarEvent) -> CalendarEvent:
        """이벤트 수정"""
        self._ensure_authenticated()
        
        try:
            # 기존 이벤트 가져오기
            calendar = self.caldav_client.calendar(url=calendar_id)
            caldav_event = calendar.event_by_url(event.id)
            
            # ICS 형식으로 업데이트
            ics_content = self._event_to_ics(event)
            caldav_event.data = ics_content
            caldav_event.save()
            
            logger.info(f"시놀로지 캘린더 이벤트 수정 완료: {event.title}")
            return event
            
        except NotFoundError:
            raise EventNotFoundError(f"이벤트를 찾을 수 없습니다: {event.id}")
        except Exception as e:
            logger.error(f"이벤트 수정 실패: {e}")
            raise CalendarServiceError(f"이벤트 수정 실패: {str(e)}")
    
    async def delete_event(self, calendar_id: str, event_id: str) -> bool:
        """이벤트 삭제"""
        self._ensure_authenticated()
        
        try:
            calendar = self.caldav_client.calendar(url=calendar_id)
            caldav_event = calendar.event_by_url(event_id)
            caldav_event.delete()
            
            logger.info(f"시놀로지 캘린더 이벤트 삭제 완료: {event_id}")
            return True
            
        except NotFoundError:
            raise EventNotFoundError(f"이벤트를 찾을 수 없습니다: {event_id}")
        except Exception as e:
            logger.error(f"이벤트 삭제 실패: {e}")
            raise CalendarServiceError(f"이벤트 삭제 실패: {str(e)}")
    
    async def get_event(self, calendar_id: str, event_id: str) -> CalendarEvent:
        """이벤트 조회"""
        self._ensure_authenticated()
        
        try:
            calendar = self.caldav_client.calendar(url=calendar_id)
            caldav_event = calendar.event_by_url(event_id)
            
            # ICS에서 CalendarEvent로 변환
            event = self._ics_to_event(caldav_event.data)
            event.id = event_id
            
            return event
            
        except NotFoundError:
            raise EventNotFoundError(f"이벤트를 찾을 수 없습니다: {event_id}")
        except Exception as e:
            logger.error(f"이벤트 조회 실패: {e}")
            raise CalendarServiceError(f"이벤트 조회 실패: {str(e)}")
    
    def _event_to_ics(self, event: CalendarEvent) -> str:
        """CalendarEvent를 ICS 형식으로 변환"""
        
        # 시간 형식 변환
        start_str = event.start_time.strftime('%Y%m%dT%H%M%S') if event.start_time else ""
        end_str = event.end_time.strftime('%Y%m%dT%H%M%S') if event.end_time else ""
        
        # 카테고리 문자열 생성
        categories_str = ",".join(event.categories) if event.categories else ""
        
        # 알림 설정
        alarm_str = ""
        if event.reminder_minutes:
            alarm_str = f"""BEGIN:VALARM
TRIGGER:-PT{event.reminder_minutes}M
ACTION:DISPLAY
DESCRIPTION:Reminder
END:VALARM"""
        
        ics_content = f"""BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Todo-Project//Synology Calendar//EN
BEGIN:VEVENT
UID:{event.id}
DTSTART:{start_str}
DTEND:{end_str}
SUMMARY:{event.title}
DESCRIPTION:{event.description or ''}
CATEGORIES:{categories_str}
STATUS:{event.status}
PRIORITY:5
CLASS:PRIVATE
{alarm_str}
END:VEVENT
END:VCALENDAR"""
        
        return ics_content
    
    def _ics_to_event(self, ics_content: str) -> CalendarEvent:
        """ICS 형식을 CalendarEvent로 변환"""
        # 간단한 ICS 파싱 (실제로는 icalendar 라이브러리 사용 권장)
        lines = ics_content.split('\n')
        
        event = CalendarEvent()
        
        for line in lines:
            line = line.strip()
            if line.startswith('SUMMARY:'):
                event.title = line[8:]
            elif line.startswith('DESCRIPTION:'):
                event.description = line[12:]
            elif line.startswith('DTSTART:'):
                try:
                    event.start_time = datetime.strptime(line[8:], '%Y%m%dT%H%M%S')
                except ValueError:
                    pass
            elif line.startswith('DTEND:'):
                try:
                    event.end_time = datetime.strptime(line[6:], '%Y%m%dT%H%M%S')
                except ValueError:
                    pass
            elif line.startswith('CATEGORIES:'):
                categories = line[11:].split(',')
                event.categories = [cat.strip() for cat in categories if cat.strip()]
            elif line.startswith('STATUS:'):
                event.status = line[7:]
        
        return event
    
    async def get_calendar_by_name(self, name: str) -> Optional[CalendarInfo]:
        """이름으로 캘린더 찾기"""
        calendars = await self.get_calendars()
        for calendar in calendars:
            if calendar.name.lower() == name.lower():
                return calendar
        return None
    
    async def create_todo_calendar_if_not_exists(self) -> CalendarInfo:
        """Todo 전용 캘린더가 없으면 생성"""
        # 기존 Todo 캘린더 찾기
        todo_calendar = await self.get_calendar_by_name("Todo")
        
        if todo_calendar:
            return todo_calendar
        
        # Todo 캘린더 생성 (시놀로지에서는 웹 인터페이스를 통해 생성해야 함)
        # 여기서는 기본 캘린더를 사용하거나 사용자에게 안내
        calendars = await self.get_calendars()
        if calendars:
            logger.info("Todo 전용 캘린더가 없어 첫 번째 캘린더를 사용합니다.")
            return calendars[0]
        
        raise CalendarServiceError("사용 가능한 캘린더가 없습니다. 시놀로지에서 캘린더를 먼저 생성해주세요.")


class SynologyMailService:
    """시놀로지 MailPlus 서비스 (캘린더 연동용)"""
    
    def __init__(self, smtp_server: str, smtp_port: int, username: str, password: str):
        self.smtp_server = smtp_server
        self.smtp_port = smtp_port
        self.username = username
        self.password = password
    
    async def send_calendar_invitation(self, to_email: str, event: CalendarEvent, ics_content: str):
        """캘린더 초대 메일 발송"""
        import smtplib
        from email.mime.multipart import MIMEMultipart
        from email.mime.text import MIMEText
        from email.mime.base import MIMEBase
        from email import encoders
        
        try:
            msg = MIMEMultipart()
            msg['From'] = self.username
            msg['To'] = to_email
            msg['Subject'] = f"📋 할일 일정: {event.title}"
            
            # 메일 본문
            body = f"""
새로운 할일이 일정에 추가되었습니다.

제목: {event.title}
시작: {event.start_time.strftime('%Y-%m-%d %H:%M') if event.start_time else '미정'}
종료: {event.end_time.strftime('%Y-%m-%d %H:%M') if event.end_time else '미정'}
설명: {event.description or ''}

이 메일의 첨부파일을 캘린더 앱에서 열면 일정이 자동으로 추가됩니다.

-- Todo-Project
            """
            
            msg.attach(MIMEText(body, 'plain', 'utf-8'))
            
            # ICS 파일 첨부
            if ics_content:
                part = MIMEBase('text', 'calendar')
                part.set_payload(ics_content.encode('utf-8'))
                encoders.encode_base64(part)
                part.add_header(
                    'Content-Disposition',
                    'attachment; filename="todo_event.ics"'
                )
                part.add_header('Content-Type', 'text/calendar; charset=utf-8')
                msg.attach(part)
            
            # SMTP 발송
            server = smtplib.SMTP(self.smtp_server, self.smtp_port)
            server.starttls()
            server.login(self.username, self.password)
            server.send_message(msg)
            server.quit()
            
            logger.info(f"캘린더 초대 메일 발송 완료: {to_email}")
            
        except Exception as e:
            logger.error(f"메일 발송 실패: {e}")
            raise CalendarServiceError(f"메일 발송 실패: {str(e)}")
