"""
アプリケーションサービス層
UIとビジネスロジックの橋渡し
"""

import sys
import os
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

from typing import Optional, List, Dict, Any
from datetime import datetime
from models.invoice_models import Invoice, Customer, InvoiceItem, DocumentType, Product
from services.repositories import InvoiceRepository, CustomerRepository
from services.pdf_generator import PdfGenerator
import logging
import json
import hashlib
import os

class InvoiceService:
    """伝票ビジネスロジック"""
    
    def __init__(self):
        self.invoice_repo = InvoiceRepository()
        self.customer_repo = CustomerRepository()
        self.pdf_generator = PdfGenerator()
        self.company_info = self._load_company_info()
    
    def _load_company_info(self) -> Dict[str, str]:
        """自社情報を読み込み"""
        # TODO: 設定ファイルから読み込み
        return {
            'id': 'SELF001',
            'name': '自社',
            'address': '',
            'phone': '',
            'registration_number': ''
        }

    def _apply_audit_fields(self, invoice: Invoice, customer: Customer):
        node_id = self.invoice_repo.get_node_id()
        prev_chain_hash = self.invoice_repo.get_last_chain_hash(node_id)

        payload_obj = {
            "schema": "invoice_payload_v1",
            "node_id": node_id,
            "uuid": invoice.uuid,
            "document_type": invoice.document_type.value,
            "invoice_number": invoice.invoice_number,
            "date": invoice.date.replace(microsecond=0).isoformat(),
            "tax_rate": 0.1,
            "tax_calc_rule": "floor(subtotal * tax_rate)",
            "customer_master_id": getattr(customer, "id", None),
            "customer_snapshot": {
                "name": customer.name,
                "formal_name": customer.formal_name,
                "address": customer.address,
                "phone": customer.phone,
            },
            "items": [
                {
                    "description": it.description,
                    "quantity": int(it.quantity),
                    "unit_price": int(it.unit_price),
                    "is_discount": bool(it.is_discount),
                }
                for it in invoice.items
            ],
            "notes": invoice.notes or "",
            "pdf_template_version": "v1",
            "company_info_version": "v1",
            "is_offset": bool(getattr(invoice, "is_offset", False)),
            "offset_target_uuid": getattr(invoice, "offset_target_uuid", None),
            "submitted_to_tax_authority": getattr(invoice, "submitted_to_tax_authority", False),
        }

        payload_json = json.dumps(payload_obj, ensure_ascii=False, separators=(",", ":"), sort_keys=True)
        payload_hash = hashlib.sha256(payload_json.encode("utf-8")).hexdigest()
        chain_hash = hashlib.sha256(f"{prev_chain_hash}:{payload_hash}".encode("utf-8")).hexdigest()

        invoice.node_id = node_id
        invoice.payload_json = payload_json
        invoice.payload_hash = payload_hash
        invoice.prev_chain_hash = prev_chain_hash
        invoice.chain_hash = chain_hash
        invoice.pdf_template_version = "v1"
        invoice.company_info_version = "v1"
        invoice.submitted_to_tax_authority = False
    
    def create_draft_invoice(self, 
                           customer: Customer, 
                           document_type: DocumentType,
                           amount: int,
                           notes: str = "") -> Optional[Invoice]:
        """下書き伝票作成
        
        Args:
            customer: 顧客情報
            document_type: 帳票種類
            amount: 金額（税抜）
            notes: 備考
        
        Returns:
            作成されたInvoice、失敗時はNone
        """
        try:
            # 下書き伝票を作成
            invoice = Invoice(
                customer=customer,
                date=datetime.now(),
                items=[],  # 下書きは空の明細で開始
                document_type=document_type,
                notes=notes,
                is_draft=True  # 下書きフラグを設定
            )
            
            # DBに保存
            saved_invoice = self.invoice_repo.save_invoice(invoice)
            
            if saved_invoice:
                logging.info(f"下書き伝票作成成功: {saved_invoice.invoice_number}")
                return saved_invoice
            else:
                logging.error("下書き伝票作成失敗")
                return None
                
        except Exception as e:
            logging.error(f"下書き伝票作成エラー: {e}")
            return None
    
    def create_invoice(self, 
                      customer: Customer, 
                      document_type: DocumentType,
                      amount: int,
                      notes: str = "",
                      items: List[InvoiceItem] = None) -> Optional[Invoice]:
        """新規伝票作成
        
        Args:
            customer: 顧客情報
            document_type: 帳票種類
            amount: 金額（税抜）
            notes: 備考
            items: 明細リスト（指定しない場合はダミーを作成）
        
        Returns:
            作成されたInvoice、失敗時はNone
        """
        try:
            # 明細作成（指定された明細を使用、なければダミーを作成）
            if items is None:
                items = [InvoiceItem(
                    description=f"{document_type.value}分",
                    quantity=1,
                    unit_price=amount
                )]
            
            # 空の明細リストの場合は保存しない（全て空の場合のみ）
            if not items or all(item.description == "" and item.quantity == 0 and item.unit_price == 0 for item in items):
                logging.warning(f"明細が空のため伝票作成を中止: {customer.name}")
                return None
            
            # 空行をフィルタリングせず、そのまま保存（ユーザーが意図した空行を保持）
            # TODO: 必要に応じて空行を除外するオプションを提供
            filtered_items = items
            
            # 伝票作成
            invoice = Invoice(
                customer=customer,
                date=datetime.now(),
                items=items,
                document_type=document_type,
                notes=notes
            )

            # --- 長期保管向け: canonical payload + hash chain ---
            # TODO: ユーザーが明示的に要求した場合のみ実行するべき
            # self._apply_audit_fields(invoice, customer)
            
            # DB保存（PDFは仮生成物なので保存の成否と切り離す）
            invoice.file_path = None
            if self.invoice_repo.save_invoice(invoice):
                logging.info(f"伝票作成成功: {invoice.invoice_number}")
            else:
                logging.error("伝票DB保存失敗")
                return None

            # PDF生成はユーザーが明示的に要求した場合のみ実行
            # TODO: 自動生成は無効化するべき
            # try:
            #     pdf_path = self.pdf_generator.generate_invoice_pdf(invoice, self.company_info)
            #     if pdf_path:
            #         invoice.file_path = pdf_path
            #         invoice.pdf_generated_at = datetime.now().replace(microsecond=0).isoformat()
            #     else:
            #         logging.warning("PDF生成失敗（DB保存は完了）")
            # except Exception as e:
            #     logging.warning(f"PDF生成例外（DB保存は完了）: {e}")

            return invoice
                
        except Exception as e:
            logging.error(f"伝票作成エラー: {e}")
            return None

    def create_offset_invoice(self, target_uuid: str, notes: str = "") -> Optional[Invoice]:
        """本伝を相殺する赤伝（マイナス伝票）を作成"""
        try:
            target = self.invoice_repo.get_invoice_by_uuid(target_uuid)
            if not target:
                logging.error(f"赤伝作成失敗: 対象uuidが見つかりません: {target_uuid}")
                return None

            # 1行で相殺（値引き行としてマイナス）
            items = [
                InvoiceItem(
                    description=f"相殺(赤伝) 対象:{target.invoice_number}",
                    quantity=1,
                    unit_price=int(target.subtotal),
                    is_discount=True,
                )
            ]

            invoice = Invoice(
                customer=target.customer,
                date=datetime.now(),
                items=items,
                document_type=target.document_type,
                notes=notes,
            )
            invoice.is_offset = True
            invoice.offset_target_uuid = target.uuid

            self._apply_audit_fields(invoice, invoice.customer)

            # DB保存（PDFは仮生成物）
            invoice.file_path = None
            if not self.invoice_repo.save_invoice(invoice):
                logging.error("赤伝DB保存失敗")
                return None

            # PDF生成（任意）
            try:
                pdf_path = self.pdf_generator.generate_invoice_pdf(invoice, self.company_info)
                if pdf_path:
                    invoice.file_path = pdf_path
                    invoice.pdf_generated_at = datetime.now().replace(microsecond=0).isoformat()
            except Exception as e:
                logging.warning(f"赤伝PDF生成例外（DB保存は完了）: {e}")

            logging.info(f"赤伝作成成功: {invoice.invoice_number} (target={target.invoice_number})")
            return invoice

        except Exception as e:
            logging.error(f"赤伝作成エラー: {e}")
            return None
    
    def submit_to_tax_authority(self, invoice_uuid: str) -> bool:
        """税務署提出済みフラグを設定し、ロックする"""
        try:
            with sqlite3.connect(self.invoice_repo.db_path) as conn:
                cursor = conn.cursor()
                cursor.execute(
                    "UPDATE invoices SET submitted_to_tax_authority = 1 WHERE uuid = ?",
                    (invoice_uuid,)
                )
                conn.commit()
                return True
        except Exception as e:
            logging.error(f"税務署提出エラー: {e}")
            return False
    
    def get_recent_invoices(self, limit: int = 50) -> List[Invoice]:
        """最近の伝票を取得"""
        return self.invoice_repo.get_all_invoices(limit)

    def regenerate_pdf(self, invoice_uuid: str) -> Optional[str]:
        """DBを正としてPDFを再生成（生成物は仮）"""
        invoice = self.invoice_repo.get_invoice_by_uuid(invoice_uuid)
        if not invoice:
            logging.error(f"PDF再生成失敗: uuidが見つかりません: {invoice_uuid}")
            return None

        # 既存のPDFパスとnotesをクリアして新規生成
        invoice.file_path = None  # 既存パスをクリア
        invoice.notes = ""  # notesもクリア（古いパスが含まれている可能性）
        
        # 会社情報は現状v1固定。将来はcompany_info_versionで分岐。
        pdf_path = self.pdf_generator.generate_invoice_pdf(invoice, self.company_info)
        if not pdf_path:
            return None
        
        # 新しいPDFパスをDBに保存
        invoice.file_path = pdf_path
        invoice.pdf_generated_at = datetime.now().replace(microsecond=0).isoformat()
        self.invoice_repo.update_invoice_file_path(invoice.uuid, pdf_path, invoice.pdf_generated_at)
        
        return pdf_path

    def delete_pdf_file(self, pdf_path: str) -> bool:
        """仮生成PDFを削除"""
        try:
            if not pdf_path:
                return True
            if os.path.exists(pdf_path):
                os.remove(pdf_path)
            return True
        except Exception as e:
            logging.warning(f"PDF削除失敗: {e}")
            return False
    
    def update_invoice(self, invoice: Invoice) -> bool:
        """伝票を更新"""
        try:
            return self.invoice_repo.update_invoice(invoice)
        except Exception as e:
            logging.error(f"伝票更新エラー: {e}")
            return False
    
    def delete_invoice_by_uuid(self, invoice_uuid: str) -> bool:
        """UUIDで伝票を削除"""
        try:
            return self.invoice_repo.delete_invoice_by_uuid(invoice_uuid)
        except Exception as e:
            logging.error(f"伝票削除エラー: {e}")
            return False
    
    def delete_invoice(self, invoice_id: int) -> bool:
        """伝票を削除"""
        return self.invoice_repo.delete_invoice(invoice_id)
    
    def get_statistics(self) -> Dict[str, Any]:
        """統計情報取得"""
        return self.invoice_repo.get_statistics()
    
    def update_company_info(self, info: Dict[str, str]) -> bool:
        """自社情報を更新"""
        try:
            self.company_info.update(info)
            # TODO: 設定ファイルに保存
            return True
        except Exception as e:
            logging.error(f"自社情報更新エラー: {e}")
            return False


class CustomerService:
    """顧客ビジネスロジック"""
    
    def __init__(self, db_path: str = "sales.db"):
        self.db_path = db_path
        self.customer_repo = CustomerRepository(db_path)
        self._customer_cache: List[Customer] = []
        self._load_customers()
    
    def create_customer(self, name: str, formal_name: str, address: str = "", phone: str = "") -> int:
        """顧客を新規作成"""
        customer = Customer(
            id=0,  # 新規はID=0
            name=name,
            formal_name=formal_name,
            address=address,
            phone=phone
        )
        success = self.customer_repo.save_customer(customer)
        if success:
            self._customer_cache.append(customer)
            logging.info(f"新規顧客登録: {formal_name}")
            return customer.id  # IDを返す
        else:
            logging.error(f"新規顧客登録失敗: {formal_name}")
            return 0  # 失敗時は0を返す
    
    def _load_customers(self):
        """顧客データを読み込み"""
        self._customer_cache = self.customer_repo.get_all_customers()
        
        # 初期データがない場合はサンプル作成
        if not self._customer_cache:
            sample_customers = [
                Customer(1, "田中商事", "田中商事株式会社", "東京都千代田区丸の内1-1-1", "03-1234-5678"),
                Customer(2, "鈴木商店", "鈴木商店", "東京都港区芝1-1-1", "03-2345-6789"),
                Customer(3, "佐藤工業", "佐藤工業株式会社", "東京都品川区東品川1-1-1", "03-3456-7890"),
            ]
            for customer in sample_customers:
                self.add_customer(customer)
            
            self._customer_cache = sample_customers
    
    def get_all_customers(self) -> List[Customer]:
        """全顧客を取得"""
        return self._customer_cache.copy()
    
    def search_customers(self, query: str) -> List[Customer]:
        """顧客を検索"""
        query = query.lower()
        return [
            c for c in self._customer_cache
            if query in c.name.lower() or 
               query in c.formal_name.lower() or
               query in c.address.lower()
        ]
    
    def add_customer(self, customer: Customer) -> bool:
        """顧客を追加"""
        success = self.customer_repo.save_customer(customer)
        if success:
            self._customer_cache.append(customer)
        return success
    
    def delete_customer(self, customer_id: int) -> bool:
        """顧客を削除"""
        # TODO: 使用されている顧客は削除不可
        self._customer_cache = [c for c in self._customer_cache if c.id != customer_id]
        return True


# サービスファクトリ
class ProductService:
    """商品ビジネスロジック"""
    
    def __init__(self):
        self.invoice_repo = InvoiceRepository()
    
    def get_all_products(self) -> List[Product]:
        """全商品を取得"""
        return self.invoice_repo.get_all_products()
    
    def save_product(self, product: Product) -> bool:
        """商品を保存（新規・更新）"""
        return self.invoice_repo.save_product(product)


class AppService:
    """アプリケーションサービス統合"""
    
    def __init__(self):
        self.invoice = InvoiceService()
        self.customer = CustomerService()
        self.product = ProductService()
    
    def get_dashboard_data(self) -> Dict[str, Any]:
        """ダッシュボード表示用データ"""
        stats = self.invoice.get_statistics()
        recent_invoices = self.invoice.get_recent_invoices(5)
        
        return {
            'total_invoices': stats['total_count'],
            'total_amount': stats['total_amount'],
            'monthly_amount': stats['monthly_amount'],
            'recent_invoices': recent_invoices,
            'customer_count': len(self.customer.get_all_customers())
        }


# 使用例
if __name__ == "__main__":
    # サービス初期化
    app_service = AppService()
    
    # ダッシュボードデータ取得
    dashboard = app_service.get_dashboard_data()
    print(f"ダッシュボード: {dashboard}")
    
    # 顧客取得
    customers = app_service.customer.get_all_customers()
    print(f"\n顧客数: {len(customers)}")
    
    if customers:
        # 伝票作成テスト
        from models.invoice_models import DocumentType
        
        invoice = app_service.invoice.create_invoice(
            customer=customers[0],
            document_type=DocumentType.INVOICE,
            amount=250000,
            notes="テスト伝票"
        )
        
        if invoice:
            print(f"\n伝票作成成功: {invoice.invoice_number}")
            print(f"合計金額: ¥{invoice.total_amount:,}")
            print(f"PDF: {invoice.file_path}")
        else:
            print("\n伝票作成失敗")
